AnyEvent-Promise

 view release on metacpan or  search on metacpan

lib/AnyEvent/Promise.pm  view on Meta::CPAN


=head1 SYNOPSIS

Avoid the evented pyramid of doom!

    use AnyEvent::Promise;
    use AnyEvent::Redis;

    my $redis = AnyEvent::Redis->new();

    my $p = promise(sub {
        $redis->get('test');
    })->then(sub {
        $redis->set('test', shift);
    })->then(sub {
        $redis->get('test');
    })->then(sub {
        say shift;
    })->catch(sub {
        say 'I failed!';
        say @_;
    })->fulfill;

=head1 DESCRIPTION

L<AnyEvent::Promise> allows evented interfaces to be chained, taking away some
of the redundancy of layering L<AnyEvent> condition variable callbacks.

A promise is created using L<AnyEvent::Promise::new|/new> or the exported
L</promise> helper function. These will both return a promise instance and add
the callback function as the start of the promise chain. Each call to L</then>
on the promise instance will add another subroutine which returns a condition
variable to the chain.

The promise callback chain won't start until L</condvar> or L</fulfill> is
called on the instance. Calling L</condvar> or L</cv> will start the callback
chain and return the promise guarding condvar, which is fulfilled after the last
callback on the chain returns. Similarily, L</fulfill> will start the chain, but
will block until the guarding condvar is fulfilled.

Errors in the callbacks can be caught by setting an exception handler via the
L</catch> method on the promise instance. This method will catch exceptions
raised from L<AnyEvent> objects and exceptions raised in blocks provided to
L</then>. If an error is encountered in the chain, an exception will be thrown
and the rest of the chain will be skipped, jumping straight to the catch
callback.

=head1 EXPORT

=head2 promise($cb)

Start promise chain with callback C<$cb>. This function is a shortcut to
L<AnyEvent::Promise::new|/new>, and returns a promise object with the callback
attached.

=cut
sub promise { AnyEvent::Promise->new(@_) }

sub import {
    no strict 'refs';  ## no critic (ProhibitNoStrict)
    *{caller() . '::promise'} = \&promise;
}

=head1 METHODS

=head2 new($cb)

Create an instance of a promise, start the chain off with callback C<$cb>. See
L</then> for information on passing in a callback and condvar.

=cut
sub new {
    my ($class, $cb) = @_;

    my $self = bless {
        guard => undef,
        initial => undef,
        fulfill => undef,
        reject => undef,
        rejected => 0
    }, $class;

    $self->{guard} = AnyEvent->condvar;
    $self->{initial} = AnyEvent->condvar;

    my $reject = AnyEvent->condvar;
    $reject->cb(sub {
        carp shift->recv;
        $self->{guard}->send;
    });
    $self->{reject} = $reject;

    $self->then($cb);

    return $self;
}

=head2 then($cb)

Add callback C<$cb> on to the promise chain.

This callback will receive the return of the previous callback -- i.e. the
callback will receive the value sent by the previous condvar directly. In order
to continue the promise chain, the callback should return a condvar.

Instead of:

    my $cv = $redis->get('test');
    $cv->cb(sub {
        my $ret = shift->recv;
        my $cv2 = $redis->set('test', $ret);
        $cv2->cb(sub {
            my $cv3 = $redis->get('test');
            $cv3->cb(sub {
                my $ret3 = shift->recv;
                printf("Got a value: %s\n", $ret3);
            });
        });
    });
    $cv->recv;



( run in 5.090 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )