Skip to content

manakai/perl-promise

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

=head1 NAME

Promise - Promise for Perl

=head1 SYNOPSIS

  use Promise;
  
  $p = Promise->new (sub {
    my ($resolve, $reject) = @_;
    some_async_action (sub {
      if ($error) {
        $reject->($error);
      } else {
        $resolve->($result);
      }
    });
  });
  $p->then (sub {
    my $result = shift;
    ...
  }, sub {
    my $error = shift;
    ...
  });

=head1 DESCRIPTION

The C<Promise> module defines a promise class which exposes methods
similar to those of the JavaScript C<Promise> class.

A promise is either to be fulfilled or to be rejected.  When the
promise is B<fulfilled> with a value, any fulfilled callback
registered with the promise is invoked with the value.  When the
promise is B<rejected> with a value, any rejected callback registered
with the promise is invoked with the value.  Any number of fulfilled
and rejected callbacks can be registered.  Callbacks can be registered
anytime, before or after fulfilling or rejecting of the promise.  Each
callback is invoked at most once for the promise.

A promise can be B<resolved>.  When a promise is resolved with another
promise, the original promise is fulfilled or rejected once the other
promise is fulfilled or rejected, with the same value.  When a promise
is resolved with a non-promise value, the promise is fulfilled with
the value.

Note that fulfilling a promise without value is equivalent to
fulfilling a promise with the value of C<undef>.

For the purpose of resolution, any Perl object with the C<then> method
defined is a promise.  That is, promise objects implementing other
Perl classes can be used in mixture with these L<Promise> objects.

=head1 METHODS

There are following class methods:

=over 4

=item $p = Promise->new (CODE)

Create a new promise object.  There must be an argument, which must be
a code reference.  It is expected that the code, when invoked, calls
either the first or the second argument to the code synchronously or
asynchronously.

To resolve the promise created, the first argument (C<$_[0]>), which
is a code reference, must be invoked.  If an argument is specified to
the code, it is used to resolve the promise.  Otherwise, C<undef> is
used to resolve the promise.

To reject the promise created, the second argument (C<$_[1]>), which
is a code reference, must be invoked.  If an argument is specified to
the code, it is used to reject the promise.  Otherwise, C<undef> is
used to reject the promise.

=item $p = Promise->resolve ($x)

Return a promise which is resolved with the value specified as the
argument.  If the argument is a promise, that promise is returned as
is.  Otherwise, a new promise is created.

=item $p = Promise->reject ($r)

Create and return a new promise which is rejected with the value
specified as the argument.

=item $p = Promise->all ([$p1, $p2, ...])

Create a new promise object, which is to be fulfilled when all of the
promises specified in the argument are fulfilled.

The argument must be an array reference (or an object which can be
evaluated as if it were an array reference).

The promise created is to be fulfilled when all of the promises are
fulfilled, with an array reference which contains the resolved values
of the values included in the array in the argument, in same order.

The promise is to be rejected when any of the promises is rejected,
with the value of the rejected promise.

=item $p = Promise->race ([$p1, $p2, ...])

Create a new promise object, which is to be fulfilled when any of the
promises specified in the argument are fulfilled.

The argument must be an array reference (or an object which can be
evaluated as if it were an array reference).

The promise is to be fulfilled or rejected when any of the promises is
fulfilled or rejected, with the value of the fulfilled or rejected
promise.

=item $p = Promise->from_cv ($cv)

Create a promise, which is resolved with the value received by the
condvar specified as the argument.

The argument is typically an L<AnyEvent::CondVar> object, though any
object compatible with L<AnyEvent::CondVar> API can be used.  The
argument must be an object with the C<cb> method with a code reference
argument, which is to be invoked with an object with C<recv> method.
If the C<recv> method returns a value (i.e. C<< $cv->send >> is
invoked), the promise is resolved with the value.  If the C<recv>
method throws (i.e. C<< $cv->croak >> is invoked), the promise is
rejected with the exception.

=back

A promise has following methods:

=over 4

=item $p2 = $p->then ($onfulfilled, $onrejected)

Register callback functions invoked when the promise is fulfilled or
rejected.  The arguments are the fulfilled and rejected callbacks, in
order.  The arguments can be omitted (or specify the C<undef> value).

The fulfilled callback is invoked when the promise is fulfilled, with
the value of the fulfillment as the argument.  The default fulfilled
callback just returns the argument.

The rejected callback is invoked when the promise is rejected, with
the value of the rejection as the argument.  The default rejection
callback just throws the argument.

The method returns a new promise.  If the fulfilled callback or the
rejected callback returns a value, the new promise is resolved with
the value.  If the fulfilled callback or the rejected callback throws
an exception, the new promise is rejected with the exception.

Examples:

  Promise->resolve (3)->then (sub { warn shift }); # 3
  Promise->reject (4)->then (sub { never }, sub { warn shift }); # 4
  ...->then (sub { die 5 })
     ->then (undef, sub { $_[0] })
     ->then (sub { warn shift });               # 5
  ...->then (sub { Promise->reject (6) })
     ->then (undef, sub { warn shift });        # 6
  ...->then (sub { die 7 })
     ->then (sub { never }, sub { die $_[0] })  # die with 7
  ...->then (sub { die 8 })
     ->then (sub { 10 })
     ->then (sub { never }, sub { warn $_[0] }) # 8

=item $p2 = $p->catch ($onrejected)

Register a callback function invoked when the promise is rejected.
The argument is the rejected callback.  This method has same effect as
C<< $p2 = $p->then (undef, $onrejected) >>.

=item $p2 = $p->finally ($onfinally)

Register a callback function invoked when the promise is fulfilled or
rejected.  The argument is the callback.

The method returns a new promise.  If the callback throws an
exception, or the callback returns a promise that is rejected with an
exception, the new promise is rejected with the exception.  Otherwise,
once the return value of the callback is resolved, the new promise is
fulfilled with the value of the fulfillment of the original promise,
or rejected with the exception of the rejection of the original
promise.

The callback can be used for e.g. closing any resource opened by
preceding callbacks.

Example:

  $database->open->then (sub {
    $database->... # or reject
  })->finally (sub {
    return $database->close;
  });

=item $cv = $p->to_cv

Create and return an L<AnyEvent::CondVar> object.  When the promise is
fulfilled, the condvar receives the result value (i.e. C<< $cv->recv
>> returns the value).  When the promise is rejected, the condvar is
croaked with the thrown value (i.e. C<< $cv->recv >> croaks with the
value).  Apparently this method requires L<AnyEvent>.

=item $p->manakai_set_handled

Mark the promise "handled", as if the C<then> method were invoked,
without creating a new promise or enqueueing handlers, supressing
"uncaught rejection" warning.  (In the ECMAScript specification terms,
this method sets the [[PromiseIsHandled]] internal slot to true.)

=item $string = $p->debug_info

Return a short character string that might be useful for debugging.

=back

All methods except for C<from_cv> and C<debug_info> act in similar way
to methods with same name in JavaScript C<Promise> API.

=head1 ERRORS

By default, a simple object, which is to be stringified into a string
containing a short description of the error with the location of the
error (e.g. C<Something's wrong at path/to/file.pl line 12345.\n>), is
used when the JavaScript C<Promise> API would use a C<TypeError>.  The
object is implementing the Perl Error Object Interface Level 1
<https://github.com/manakai/perl-web-dom/blob/master/lib/Web/DOM/Error.pod#error-object-api>.

By setting C<$Promise::CreateTypeError> variable to a code reference,
any value can be used as an exception instead of the string.  The code
or method is invoked with the short description as the first method
argument (C<$_[1]>) and is expected to return a value that is to be
used as an exception.  It is expected not to throw any exception.

=head1 EVENT LOOP

Fulfilled and rejected callbacks are expected to be invoked by queuing
them to some kind of event scheduling mechanism.

By default, this is implemented using L<AnyEvent>.  By setting
C<$Promise::Enqueue> variable to a code reference, any event
scheduling mechanism can be used instead.  The code or method is
invoked with the code reference as the first method argument
(C<$_[1]>).  It is expected not to throw any exception.  It is
expected that the code reference is enqueued and to be invoked later.

If the L<AnyEvent> default is unchanged and promises are used to write
a standalone application, the condvar returned by the C<to_cv> method
of the last promise can be used to wait for receiving, which
effectively invokes the main loop of the application:

  use MyPromisedApp;
  MyPromisedApp->some_process->then (sub {
    ...
  })->then (sub {
    ...
  })->to_cv->recv;

=head1 DEPENDENCY

The module requires Perl 5.8 or later and L<Carp> (which is a core
module).

By default the module also requires L<AnyEvent>.  However they are not
required when event loop handlers are replaced as described in
previous sections.

Otherwise the module has no dependency.  The module can be used by
simply copying the module file into your Perl execution environment,
or by adding the Git repository as a submodule of your Perl
application.

=head1 SEE ALSO

ECMAScript Language Specification
<https://tc39.github.io/ecma262/#sec-promise-objects>.

There are a number of promise-based Perl modules.  For convinience,
the same Git repository as this module contains L<Promised::Flow>,
which abstracts common promise-based program control flows.

L<AbortController>, L<AbortSignal>.

=head1 HISTORY

This repository was originally located at
<https://github.com/wakaba/perl-promise>, which has been transferred
to the manaki project on October 7, 2021.

=head1 AUTHOR

Wakaba <wakaba@suikawiki.org>.

=head1 LICENSE

Copyright 2014-2021 Wakaba <wakaba@suikawiki.org>.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut