# NAME Promise::ES6 - ES6-style promises in Perl # SYNOPSIS $Promise::ES6::DETECT_MEMORY_LEAKS = 1; my $promise = Promise::ES6->new( sub { my ($resolve_cr, $reject_cr) = @_; # .. } ); my $promise2 = $promise->then( sub { .. }, sub { .. } ); my $promise3 = $promise->catch( sub { .. } ); my $promise4 = $promise->finally( sub { .. } ); my $resolved = Promise::ES6->resolve(5); my $rejected = Promise::ES6->reject('nono'); my $all_promise = Promise::ES6->all( \@promises ); my $race_promise = Promise::ES6->race( \@promises ); # DESCRIPTION This module provides a Perl implementation of [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises), a useful pattern for coordinating asynchronous tasks. Unlike most other promise implementations on CPAN, this module mimics ECMAScript 6窶冱 [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) class. As the SYNOPSIS above shows, you can thus use patterns from JavaScript in Perl with only minimal changes needed to accommodate language syntax. This is a rewrite of an earlier module, [Promise::Tiny](https://metacpan.org/pod/Promise::Tiny). It fixes several bugs and superfluous dependencies in the original. # INTERFACE NOTES - Promise resolutions and rejections accept exactly one argument, not a list. - Unhandled rejections are reported via `warn()`. (See below for details.) # COMPATIBILITY Right now this doesn窶冲 try for interoperability with other promise classes. If that窶冱 something you want, make a feature request. # UNHANDLED REJECTIONS As of version 0.05, unhandled rejections prompt a warning _only_ if one of the following is true: - 1) The unhandled rejection happens outside of the constructor. - 2) The unhandled rejection happens via an uncaught exception (even within the constructor). # SYNCHRONOUS OPERATION In JavaScript, the following 窶ヲ Promise.resolve().then( () => console.log(1) ); console.log(2); 窶ヲ will log `2` then `1` because JavaScript窶冱 `then()` defers execution of its callbacks until the end of the current iteration through JavaScript窶冱 event loop. Perl, of course, has no built-in event loop. This module窶冱 `then()` method, thus, when called on a promise that is already 窶徭ettled窶� (i.e., not pending), will run the appropriate callback _immediately_. That means that this: Promise::ES6->resolve(0)->then( sub { print 1 } ); print 2; 窶ヲ will print `12` instead of `21`. This is an intentional divergence from [the Promises/A+ specification](https://promisesaplus.com/#point-34). A key advantage of this design is that Promise::ES6 instances can abstract over whether a given function works synchronously or asynchronously. If you want a Promises/A+-compliant implementation, look at [Promise::ES6::IOAsync](https://metacpan.org/pod/Promise::ES6::IOAsync), [Promise::ES6::AnyEvent](https://metacpan.org/pod/Promise::ES6::AnyEvent), or one of the alternatives that that module窶冱 documentation suggests. # CANCELLATION Promises have never provided a standardized solution for cancellation窶琶.e., aborting an in-process operation. So, if you need this functionality, you窶冤l have to implement it yourself. Two ways of doing this are: - Subclass Promise::ES6 and provide cancellation logic in your subclass. See [DNS::Unbound::AsyncQuery](https://metacpan.org/pod/DNS::Unbound::AsyncQuery)窶冱 implementation for an example of this. - Implement the cancellation on the object that creates your promises. This is probably the more straightforward approach but requires that there be some object or ID besides the promise that uniquely identifies the action to be canceled. See [Net::Curl::Promiser](https://metacpan.org/pod/Net::Curl::Promiser) for an example of this approach. You窶冤l need to decide if it makes more sense for your application to leave a canceled query in the 窶徘ending窶� state or to resolve or reject it. All things being equal, I feel the first approach is the most intuitive. # MEMORY LEAKS It窶冱 easy to create inadvertent memory leaks using promises in Perl. Here are a few 窶徘ointers窶� (heh) to bear in mind: - As of version 0.07, any Promise::ES6 instances that are created while `$Promise::ES6::DETECT_MEMORY_LEAKS` is set to a truthy value are 窶徑eak-detect-enabled窶�, which means that if they survive until their original process窶冱 global destruction, a warning is triggered. - If your application needs recursive promises (e.g., to poll iteratively for completion of a task), the `current_sub` feature (i.e., `__SUB__`) may help you avoid memory leaks. (See this module窶冱 source code for a substitute that works with pre-5.16 perls.) - Garbage collection before Perl 5.18 seems to have been buggy. If you work with such versions and end up chasing leaks, try manually deleting as many references/closures as possible. See `t/race_success.t` for a notated example. You may also (counterintuitively, IMO) find that this: my ($resolve, $reject); my $promise = Promise::ES6->new( sub { ($resolve, $reject) = @_ } ); # 窶ヲ etc. 窶ヲツ�works better than: my $promise = Promise::ES6->new( sub { my ($resolve, $reject) = @_; # 窶ヲ etc. } ); # SEE ALSO If you窶决e not sure of what promises are, there are several good introductions to the topic. You might start with [this one](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Promise::ES6 serves much the same role as [Future](https://metacpan.org/pod/Future) but exposes a standard, minimal, cross-language API rather than a proprietary (large) one. CPAN contains a number of other modules that implement promises. I think mine is the nicest :), but YMMV. Enjoy! # LICENSE & COPYRIGHT Copyright 2019 Gasper Software Consulting. This library is licensed under the same terms as Perl itself.