Promises(3)
π Promises π
okay so bestie, this is the Promises(3) page β user contributed perl documentation, and we're about to make async perl make sense. π¦
πΈ NAME πΈ
Promises β an implementation of Promises in Perl. periodt. π
β¨ VERSION β¨
version 1.05
π SYNOPSIS π
like, here's the whole vibe in one go:
use AnyEvent::HTTP;
use JSON::XS qw[ decode_json ];
use Promises qw[ collect deferred ];
sub fetch_it {
my ($uri) = @_;
my $d = deferred;
http_get $uri => sub {
my ($body, $headers) = @_;
$headers->{Status} == 200
? $d->resolve( decode_json( $body ) )
: $d->reject( $body )
};
$d->promise;
}
my $cv = AnyEvent->condvar;
collect(
fetch_it('http://rest.api.example.com/-/product/12345'),
fetch_it('http://rest.api.example.com/-/product/suggestions?for_sku=12345'),
fetch_it('http://rest.api.example.com/-/product/reviews?for_sku=12345'),
)->then(
sub {
my ($product, $suggestions, $reviews) = @_;
$cv->send({
product => $product,
suggestions => $suggestions,
reviews => $reviews,
})
},
sub { $cv->croak( 'ERROR' ) }
);
my $all_product_info = $cv->recv;
π DESCRIPTION π
So this module is an implementation of the "Promise/A+" pattern for asynchronous programming. And the whole point of promises, bestie, is that they're a way to deal with the callback spaghetti that async programs keep producing. No more noodle code. πβ‘οΈβ¨
π FUTURE BACKWARDS COMPATIBILITY WARNING π
The version of this module is getting bumped up to 0.90 as the first step on the road to 1.0, and the goal there is full Promises/A+ spec compatibility. This is a departure from the previous goal of being compatible with the Promises/A spec, which means behavior may change in subtle ways (they'll try to document this completely and clearly whenever possible, promise π€).
It is HIGHLY recommended that you test things very thoroughly before upgrading to this version. like, actually. test it. π
π BACKWARDS COMPATIBILITY WARNING π
In versions up to and including 0.08 there was a bug in how rejected promises were handled. According to the spec, a rejected callback can:
- Rethrow the exception, in which case the next rejected handler in the chain would be called, or
- Handle the exception (by not
dieing), in which case the next resolved handler in the chain would be called.
In previous versions of Promises, this last step was handled incorrectly: a rejected handler had no way of handling the exception. Once a promise was rejected, only rejected handlers in the chain would be called. π
π Relation to the various Perl event loops π
This module is actually Event Loop agnostic β the SYNOPSIS up there uses AnyEvent::HTTP, but that's just an example, bestie. It can work with any of the existing event loops out on CPAN. Over the next few releases they'll try to add documentation illustrating each of the different event loops and how best to use Promises with them. π
π§ Relation to the Promise/A spec π§
With some differences, they're following the API spec called "Promise/A" (and the clarification that's called "Promise/A+") which was created by the Node.JS community. For the most part this is the same API implemented in the latest jQuery and in the YUI Deferred plug-in (though some purists argue that they both got it wrong β google it if you care π). They differ in some respects, mostly because Perl idioms and best practices are not the same as Javascript idioms and best practices.
But the one important difference that should be noted: "Promise/A+" strongly suggests that the callbacks given to then should be run asynchronously (meaning in the next turn of the event loop). They do not do this by default, because doing so would bind them to a given event loop implementation, which they very much want to avoid. However, you can now specify an event loop "backend" when using Promises, and assuming a Deferred backend has been written, it will provide this feature accordingly. β¨
π«Ά Using a Deferred backend π«Ά
As mentioned above, the default Promises::Deferred class calls the success or error then() callback synchronously, because it isn't tied to a particular event loop. However, it is recommended that you use the appropriate Deferred backend for whichever event loop you are running.
Typically an application uses a single event loop, so all Promises should use the same event-loop. Module implementers should just use the Promises class directly:
package MyClass;
use Promises qw(deferred collect);
End users should specify which Deferred backend they wish to use. For instance if you're using AnyEvent, you can do:
use Promises backend => ['AnyEvent'];
use MyClass;
The Promises returned by MyClass will automatically use whichever event loop AnyEvent is using. slay. π
See:
Promises::Deferred::AEPromises::Deferred::AnyEventPromises::Deferred::EVPromises::Deferred::MojoPromises::Deferred::IO::Async
π¦ Relation to Promises/Futures in Scala π¦
Scala has a notion of Promises and an associated idea of Futures as well. The differences and similarities between this module and the Promises found in Scalar are highlighted in depth in a cookbook entry below. π
π Cookbook π
Promises::Cookbook::GentleIntro
Read this first! This cookbook provides a step-by-step explanation of how Promises work and how to use them.
Promises::Cookbook::SynopsisBreakdown
This breaks down the example in the SYNOPSIS and walks through much of the details of Promises and how they work.
Promises::Cookbook::TIMTOWTDI
Promise are just one of many ways to do async programming, this entry takes the Promises SYNOPSIS again and illustrates some counter examples with various modules.
Promises::Cookbook::ChainingAndPipelining
One of the key benefits of Promises is that it retains much of the flow of a synchronous program, this entry illustrates that and compares it with a synchronous (or blocking) version.
Promises::Cookbook::Recursion
This entry explains how to keep the stack under control when using Promises recursively.
Promises::Cookbook::ScalaFuturesComparison
This entry takes some examples of Futures in the Scala language and translates them into Promises. This entry also showcases using Promises with Mojo::UserAgent.
β EXPORTS β
"deferred"β This just creates an instance of thePromises::Deferredclass, it is purely for convenience.
Can take a coderef, which will be dealt with as a "then" argument.
my $promise = deferred sub {
... do stuff ...
return $something;
};
# equivalent to
my $dummy = deferred;
my $promise = $dummy->then(sub {
... do stuff ...
return $something;
});
$dummy->resolve;
resolved( @values )β Creates an instance ofPromises::Deferredresolved with the provided@values. Purely a shortcut for
my $promise = deferred;
$promise->resolve(@values);
rejected( @values )β Creates an instance ofPromises::Deferredrejected with the provided@values. Purely a shortcut for
my $promise = deferred;
$promise->reject(@values);
collect( @promises )β Accepts a list ofPromises::Promiseobjects and then returns aPromises::Promiseobject which will be called once all the@promiseshave completed (either as an error or as a success).
The eventual result of the returned promise object will be an array of all the results of each of the @promises in the order in which they where passed to "collect" originally, wrapped in arrayrefs, or the first error if at least one of the promises fail.
If "collect" is passed a value that is not a promise, it'll be wrapped in an arrayref and passed through.
my $p1 = deferred;
my $p2 = deferred;
$p1->resolve(1);
$p2->resolve(2,3);
collect(
$p1,
'not a promise',
$p2,
)->then(sub{
print join ' : ', map { join ', ', @$_ } @_; # => "1 : not a promise : 2, 3"
})
collect_hash( @promises )β Like"collect", but flatten its returned arrayref into a single hash-friendly list.
"collect_hash" can be useful to a structured hash instead of a long list of promise values.
For example,
my $id = 12345;
collect(
fetch_it("http://rest.api.example.com/-/product/$id"),
fetch_it("http://rest.api.example.com/-/product/suggestions?for_sku=$id"),
fetch_it("http://rest.api.example.com/-/product/reviews?for_sku=$id"),
)->then(
sub {
my ($product, $suggestions, $reviews) = @_;
$cv->send({
product => $product,
suggestions => $suggestions,
reviews => $reviews,
id => $id
})
},
sub { $cv->croak( 'ERROR' ) }
);
could be rewritten as
my $id = 12345;
collect_hash(
id => $id,
product => fetch_it("http://rest.api.example.com/-/product/$id"),
suggestions => fetch_it("http://rest.api.example.com/-/product/suggestions?for_sku=$id"),
reviews => fetch_it("http://rest.api.example.com/-/product/reviews?for_sku=$id"),
)->then(
sub {
my %results = @_;
$cv->send(\%results);
},
sub { $cv->croak( 'ERROR' ) }
);
Note that all promise values of the key/value pairs passed to "collect_hash" must return a scalar or nothing, as returning more than one value would mess up the returned hash format. If a promise does return more than one value, "collect_hash" will consider it as having failed.
If you know that a promise can return more than one value, you can do:
my $collected = collect_hash(
this => $promise_returning_scalar,
that => $promise_returning_list->then(sub{ [ @_ ] } ),
);
okay bestie that's part 1 down β part 2 has the rest, don't go anywhere π
β¨ SEE ALSO β¨
okay bestie, if you wanna go down the rabbit hole and read the ~lore~, here's the reading list π
π Promises in General π
- You're Missing the Point of Promises β the classic callout, link:
http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/ - Systems Programming at Twitter β talk vibes, link:
http://monkey.org/~marius/talks/twittersystems/ - SIP-14 - Futures and Promises β Scala said let's do async, link:
http://docs.scala-lang.org/sips/pending/futures-promises.html - Promises/A+ spec β the spec that ate, link:
http://promises-aplus.github.io/promises-spec/ - Promises/A spec β the original, link:
http://wiki.commonjs.org/wiki/Promises/A
πΈ Perl Alternatives πΈ
Futureβ another Perl promise-ish module πMojo::Promiseβ Part of theMojoliciouspackage. π¦Promise::ES6β ES6-style promises in Perl β¨Promise::Tinyβ tiny promise energy πAnyEvent::XSPromisesβ promises for the AnyEvent girlies πPromise::XSβ XS-speed promises, no cap π₯
π AUTHOR π
This whole thing was written by Stevan Little β Stevan Little <stevan.little@iinteractive.com> π«Ά
π COPYRIGHT AND LICENSE π
This software is copyright (c) 2025, 2017, 2014, 2012 by Infinity Interactive, Inc. π
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. π
and that's the page, bestie!! this is from perl v5.42.0, dated 2025-07-25, and it's the Promises(3) manual page. periodt π
β¨
π¦ where this page comes from
- fedora 43: perl-Promises 1.05-2.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms