1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
# 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', 'bar' );
$deferred->reject( 'oh', 'no!' );
# Give this to your caller:
my $promise = $deferred->promise();
The following aggregator functions are exposed:
# Resolves with a list of arrayrefs, one per promise.
# Rejects with the results from the first rejected promise.
# Non-promises will be passed through as resolve values.
my $all_p = Promise::XS::all( $promise1, $promise2, 'abc' .. );
# Resolves/rejects with the results 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('it’s', 'bad');
All of `Promise::XS`’s static functions may be exported at load time,
e.g., `use Promise::XS qw(deferred)`.
# DESCRIPTION
<div>
<a href='https://coveralls.io/github/FGasper/p5-Promise-XS?branch=master'><img src='https://coveralls.io/repos/github/FGasper/p5-Promise-XS/badge.svg?branch=master' alt='Coverage Status' /></a>
</div>
This module exposes a Promise interface with its major parts
implemented in XS for speed. It is a fork and refactor of
[AnyEvent::XSPromises](https://metacpan.org/pod/AnyEvent%3A%3AXSPromises). That module’s interface, a “bare-bones”
subset of that from [Promises](https://metacpan.org/pod/Promises), is retained.
# STATUS
This module is stable, well-tested, and suitable for production use.
# DIFFERENCES FROM ECMASCRIPT PROMISES
This library is built for compatibility with pre-existing Perl promise
libraries. It thus exhibits some salient differences from how
ECMAScript promises work:
- Neither the `resolve()` method of deferred objects
nor the `resolved()` convenience function define behavior when given
a promise object.
- The `all()` and `race()` functions accept a list of promises,
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 );
- Promise resolutions and rejections may contain multiple values.
(But see ["AVOID MULTIPLES"](#avoid-multiples) below.)
See [Promise::ES6](https://metacpan.org/pod/Promise%3A%3AES6) for an interface that imitates ECMAScript promises
more closely.
# AVOID MULTIPLES
For compatibility with preexisting Perl promise libraries, Promise::XS
allows a promise to resolve or reject with multiple values. This behavior,
while eminently “perlish”, allows for some weird cases where the relevant
standards don’t apply: for example, what happens if multiple promises are
returned from a promise callback? Or even just a single promise plus extra
returns?
Promise::XS tries to help you catch such cases by throwing a warning
if multiple return values from a callback contain a promise as the
first member. For best results, though—and consistency with promise
implementations outside Perl—resolve/reject all promises with _single_
values.
# DIFFERENCES FROM [Promises](https://metacpan.org/pod/Promises) ET AL.
## Empty or uninitialized rejection values
Perl helpfully warns (under the `warnings` pragma, anyhow) when you
`die(undef)` since an uninitialized value isn’t useful as an error report
and likely indicates a problem in the error-handling logic.
Promise rejections fulfill the same role in asynchronous code that
exceptions do in synchronous code. Thus, Promise::XS mimics Perl’s behavior:
if a rejection value list lacks a defined value, a warning is thrown. This
can happen if the value list is either empty or contains exclusively
uninitialized values.
## `finally()`
This module implements ECMAScript’s `finally()` interface, which differs
from that in some other Perl promise implementations.
Given the following …
my $new = $p->finally( $callback );
- `$callback` receives _no_ arguments.
- If `$callback` returns anything but a single, rejected promise,
`$new` has the same status as `$p`.
- If `$callback` throws, or if it returns a single, rejected promise,
`$new` is rejected with the relevant value(s).
# ASYNC/AWAIT SUPPORT
This module is [Promise::AsyncAwait](https://metacpan.org/pod/Promise%3A%3AAsyncAwait)-compatible.
Once you load that module you can do nifty stuff like:
use Promise::AsyncAwait;
async sub do_stuff {
return 1 + await fetch_number_p();
}
my $one_plus_number = await do_stuff();
… which roughly equates to:
sub do_stuff {
return fetch_number_p()->then( sub { 1 + $foo } );
}
do_stuff->then( sub {
$one_plus_number = shift;
} );
**NOTE:** As of this writing, DEBUGGING-enabled perls trigger assertion
failures in [Future::AsyncAwait](https://metacpan.org/pod/Future%3A%3AAsyncAwait) (which underlies [Promise::AsyncAwait](https://metacpan.org/pod/Promise%3A%3AAsyncAwait)).
If you’re not sure what that means, you probably don’t need to worry. :)
# EVENT LOOPS
By default this library uses no event loop. This is a generally 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. Following this pattern facilitates use of recursive
promises without exceeding call stack limits.
To achieve full Promises/A+ compliance it’s necessary to integrate with
an event loop interface. This library supports three such interfaces:
- [AnyEvent](https://metacpan.org/pod/AnyEvent):
Promise::XS::use_event('AnyEvent');
- [IO::Async](https://metacpan.org/pod/IO%3A%3AAsync) - note the need for an [IO::Async::Loop](https://metacpan.org/pod/IO%3A%3AAsync%3A%3ALoop) instance
as argument:
Promise::XS::use_event('IO::Async', $loop_object);
- [Mojo::IOLoop](https://metacpan.org/pod/Mojo%3A%3AIOLoop):
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.
# 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](https://metacpan.org/pod/Promise%3A%3AXS%3A%3APromise) 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](https://metacpan.org/pod/Promise%3A%3AXS%3A%3APromise).) This can be useful, e.g., for implementing
mid-flight controls like cancellation.
# TODO
- `all()` and `race()` should ideally be implemented in XS.
# 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](https://metacpan.org/pod/AnyEvent%3A%3AXSPromises) and [Promises](https://metacpan.org/pod/Promises), you may like [Promise::ES6](https://metacpan.org/pod/Promise%3A%3AES6),
which mimics [ECMAScript’s “Promise” class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 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.
|