NAME
Promise::XS - Fast promises in Perl
SYNOPSIS
use Promise::XS ();
my $deferred = Promise::XS::deferred();
# Do one of these once you have the result of your operation:
$deferred->resolve( 'foo' );
$deferred->reject( 'oh no!' );
# Give this to your caller:
my $promise = $deferred->promise();
The following aggregator functions are exposed:
# Resolves with an arrayref, one item per promise.
# Rejects with the results from the first rejected promise.
my $all_p = Promise::XS::all( $promise1, $promise2, .. );
# Resolves/rejects with the result from the first
# resolved or rejected promise.
my $race_p = Promise::XS::race( $promise3, $promise4, .. );
For compatibility with preexisting libraries, all()
may also be called as collect()
.
The following also exist:
my $pre_resolved_promise = Promise::XS::resolved('already done');
my $pre_rejected_promise = Promise::XS::rejected('badbad');
All of Promise::XS
’s static functions may be exported at load time, e.g., use Promise::XS qw(deferred)
.
DESCRIPTION
This module exposes a Promise interface with its major parts implemented in XS for speed. It is a fork and refactor of AnyEvent::XSPromises, with some significant interface changes.
STATUS
This module should be fairly stable but is still relatively untested since the fork from AnyEvent::XSPromises. Significant breaking changes happened from version 0.07 to 0.08. Such changes aren’t expected to be needed again, but that’s not guaranteed. Caveat emptor.
PROMISE INTERFACE
The core functionality derives from the Promises/A+ specification standard. (See "EVENT LOOPS" below for one important difference.) Its finally()
implementation derives from ECMAScript promises.
Promise callbacks: list vs. scalar context
Most Perl promise libraries allow promises to resolve or reject with multiple values. This is eminently “perlish”, but what do we do if a plural return includes a promise? Neither Promises/A+ nor ECMAScript’s promise standard describes how to handle this scenario, and there’s no “obvious” solution otherwise. We could simply ignore the “extra” inputs, but what if one of those “extras” is itself a promise? What if there’s only one promise, but it’s not the first item returned?
All of these scenarios allow for subtle bugs to arise. To avoid that, this library executes all callbacks in scalar context. Besides avoiding the “problem” states described above, this also matches both Promises/A+ and ECMAScript standards. The divergence from preexisting Perl promise libraries like Mojo::Promise, Promises, and AnyEvent::XSPromises, is regrettable but seems a “lesser evil” overall.
Additional notes
Neither the
resolve()
method of deferred objects nor theresolved()
convenience function define behavior when given a promise object. Don’t do it.The
all()
andrace()
functions accept a list, not a “scalar-array-thing” (ECMAScript “arrays” being what in Perl we call “array references”). So whereas in ECMAScript you do:Promise.all( [ promise1, promise2 ] );
… in this library it’s:
Promise::XS::all( $promise1, $promise2 );
See Promise::ES6 for an interface that imitates ECMAScript promises more closely.
EVENT LOOPS
By default this library uses no event loop. This is a perfectly usable configuration; however, it’ll be a bit different from how promises usually work in evented contexts (e.g., JavaScript) because callbacks will execute immediately rather than at the end of the event loop as the Promises/A+ specification requires.
To achieve full Promises/A+ compliance it’s necessary to integrate with an event loop interface. This library supports three such interfaces:
-
Promise::XS::use_event('AnyEvent');
IO::Async - note the need for an IO::Async::Loop instance as argument:
Promise::XS::use_event('IO::Async', $loop_object);
-
Promise::XS::use_event('Mojo::IOLoop');
Note that all three of the above are event loop interfaces. They aren’t event loops themselves, but abstractions over various event loops. See each one’s documentation for details about supported event loops.
REMINDER: There’s no reason why promises need an event loop; it just satisfies the Promises/A+ convention.
MEMORY LEAK DETECTION
Any promise created while $Promise::XS::DETECT_MEMORY_LEAKS
is truthy will throw a warning if it survives until global destruction.
SUBCLASSING
You can re-bless a Promise::XS::Promise instance into a different class, and then()
, catch()
, and finally()
will assign their newly-created promise into that other class. (It follows that the other class must subclass Promise::XS::Promise.) This can be useful, e.g., for implementing mid-flight controls like cancellation.
TODO
all()
andrace()
should be implemented in XS, as shouldresolved()
andrejected()
.
KNOWN ISSUES
Interpreter-based threads may or may not work.
This module interacts badly with Perl’s fork() implementation on Windows. There may be a workaround possible, but none is implemented for now.
SEE ALSO
Besides AnyEvent::XSPromises and Promises, you may like Promise::ES6, which mimics ECMAScript’s “Promise” class as much as possible. It can even (experimentally) use this module as a backend, which helps but is still significantly slower than using this module directly.