1Promises(3) User Contributed Perl Documentation Promises(3)
2
3
4
6 Promises - An implementation of Promises in Perl
7
9 version 1.04
10
12 use AnyEvent::HTTP;
13 use JSON::XS qw[ decode_json ];
14 use Promises qw[ collect deferred ];
15
16 sub fetch_it {
17 my ($uri) = @_;
18 my $d = deferred;
19 http_get $uri => sub {
20 my ($body, $headers) = @_;
21 $headers->{Status} == 200
22 ? $d->resolve( decode_json( $body ) )
23 : $d->reject( $body )
24 };
25 $d->promise;
26 }
27
28 my $cv = AnyEvent->condvar;
29
30 collect(
31 fetch_it('http://rest.api.example.com/-/product/12345'),
32 fetch_it('http://rest.api.example.com/-/product/suggestions?for_sku=12345'),
33 fetch_it('http://rest.api.example.com/-/product/reviews?for_sku=12345'),
34 )->then(
35 sub {
36 my ($product, $suggestions, $reviews) = @_;
37 $cv->send({
38 product => $product,
39 suggestions => $suggestions,
40 reviews => $reviews,
41 })
42 },
43 sub { $cv->croak( 'ERROR' ) }
44 );
45
46 my $all_product_info = $cv->recv;
47
49 This module is an implementation of the "Promise/A+" pattern for
50 asynchronous programming. Promises are meant to be a way to better deal
51 with the resulting callback spaghetti that can often result in
52 asynchronous programs.
53
55 The version of this module is being bumped up to 0.90 as the first step
56 towards 1.0 in which the goal is to have full Promises/A+ spec
57 compatibility. This is a departure to the previous goal of being
58 compatible with the Promises/A spec, this means that behavior may
59 change in subtle ways (we will attempt to document this completely and
60 clearly whenever possible).
61
62 It is HIGHLY recommended that you test things very thoroughly before
63 upgrading to this version.
64
66 In version up to and including 0.08 there was a bug in how rejected
67 promises were handled. According to the spec, a rejected callback can:
68
69 • Rethrow the exception, in which case the next rejected handler in
70 the chain would be called, or
71
72 • Handle the exception (by not "die"ing), in which case the next
73 resolved handler in the chain would be called.
74
75 In previous versions of Promises, this last step was handled
76 incorrectly: a rejected handler had no way of handling the exception.
77 Once a promise was rejected, only rejected handlers in the chain would
78 be called.
79
80 Relation to the various Perl event loops
81 This module is actually Event Loop agnostic, the SYNOPSIS above uses
82 AnyEvent::HTTP, but that is just an example, it can work with any of
83 the existing event loops out on CPAN. Over the next few releases I will
84 try to add in documentation illustrating each of the different event
85 loops and how best to use Promises with them.
86
87 Relation to the Promise/A spec
88 We are, with some differences, following the API spec called
89 "Promise/A" (and the clarification that is called "Promise/A+") which
90 was created by the Node.JS community. This is, for the most part, the
91 same API that is implemented in the latest jQuery and in the YUI
92 Deferred plug-in (though some purists argue that they both go it wrong,
93 google it if you care). We differ in some respects to this spec, mostly
94 because Perl idioms and best practices are not the same as Javascript
95 idioms and best practices. However, the one important difference that
96 should be noted is that "Promise/A+" strongly suggests that the
97 callbacks given to "then" should be run asynchronously (meaning in the
98 next turn of the event loop). We do not do this by default, because
99 doing so would bind us to a given event loop implementation, which we
100 very much want to avoid. However we now allow you to specify an event
101 loop "backend" when using Promises, and assuming a Deferred backend has
102 been written it will provide this feature accordingly.
103
104 Using a Deferred backend
105 As mentioned above, the default Promises::Deferred class calls the
106 success or error then() callback synchronously, because it isn't tied
107 to a particular event loop. However, it is recommended that you use
108 the appropriate Deferred backend for whichever event loop you are
109 running.
110
111 Typically an application uses a single event loop, so all Promises
112 should use the same event-loop. Module implementers should just use the
113 Promises class directly:
114
115 package MyClass;
116 use Promises qw(deferred collect);
117
118 End users should specify which Deferred backend they wish to use. For
119 instance if you are using AnyEvent, you can do:
120
121 use Promises backend => ['AnyEvent'];
122 use MyClass;
123
124 The Promises returned by MyClass will automatically use whichever event
125 loop AnyEvent is using.
126
127 See:
128
129 •
130 Promises::Deferred::AE
131
132 •
133 Promises::Deferred::AnyEvent
134
135 •
136 Promises::Deferred::EV
137
138 •
139 Promises::Deferred::Mojo
140
141 •
142 Promises::Deferred::IO::Async
143
144 Relation to Promises/Futures in Scala
145 Scala has a notion of Promises and an associated idea of Futures as
146 well. The differences and similarities between this module and the
147 Promises found in Scalar are highlighted in depth in a cookbook entry
148 below.
149
150 Cookbook
151 Promises::Cookbook::GentleIntro
152 Read this first! This cookbook provides a step-by-step explanation of
153 how Promises work and how to use them.
154
155 Promises::Cookbook::SynopsisBreakdown
156 This breaks down the example in the SYNOPSIS and walks through much of
157 the details of Promises and how they work.
158
159 Promises::Cookbook::TIMTOWTDI
160 Promise are just one of many ways to do async programming, this entry
161 takes the Promises SYNOPSIS again and illustrates some counter
162 examples with various modules.
163
164 Promises::Cookbook::ChainingAndPipelining
165 One of the key benefits of Promises is that it retains much of the
166 flow of a synchronous program, this entry illustrates that and
167 compares it with a synchronous (or blocking) version.
168
169 Promises::Cookbook::Recursion
170 This entry explains how to keep the stack under control when using
171 Promises recursively.
172
173 Promises::Cookbook::ScalaFuturesComparison
174 This entry takes some examples of Futures in the Scala language and
175 translates them into Promises. This entry also showcases using
176 Promises with Mojo::UserAgent.
177
179 "deferred"
180 This just creates an instance of the Promises::Deferred class it is
181 purely for convenience.
182
183 Can take a coderef, which will be dealt with as a "then" argument.
184
185 my $promise = deferred sub {
186 ... do stuff ...
187
188 return $something;
189 };
190
191 # equivalent to
192
193 my $dummy = deferred;
194
195 my $promise = $dummy->then(sub {
196 ... do stuff ...
197
198 return $something;
199 });
200
201 $dummy->resolve;
202
203 resolved( @values )
204 Creates an instance of Promises::Deferred resolved with the
205 provided @values. Purely a shortcut for
206
207 my $promise = deferred;
208 $promise->resolve(@values);
209
210 rejected( @values )
211 Creates an instance of Promises::Deferred rejected with the
212 provided @values. Purely a shortcut for
213
214 my $promise = deferred;
215 $promise->reject(@values);
216
217 collect( @promises )
218 Accepts a list of Promises::Promise objects and then returns a
219 Promises::Promise object which will be called once all the
220 @promises have completed (either as an error or as a success).
221
222 The eventual result of the returned promise object will be an array
223 of all the results of each of the @promises in the order in which
224 they where passed to "collect" originally, wrapped in arrayrefs, or
225 the first error if at least one of the promises fail.
226
227 If "collect" is passed a value that is not a promise, it'll be
228 wrapped in an arrayref and passed through.
229
230 my $p1 = deferred;
231 my $p2 = deferred;
232 $p1->resolve(1);
233 $p2->resolve(2,3);
234
235 collect(
236 $p1,
237 'not a promise',
238 $p2,
239 )->then(sub{
240 print join ' : ', map { join ', ', @$_ } @_; # => "1 : not a promise : 2, 3"
241 })
242
243 collect_hash( @promises )
244 Like "collect", but flatten its returned arrayref into a single
245 hash-friendly list.
246
247 "collect_hash" can be useful to a structured hash instead of a long
248 list of promise values.
249
250 For example,
251
252 my $id = 12345;
253
254 collect(
255 fetch_it("http://rest.api.example.com/-/product/$id"),
256 fetch_it("http://rest.api.example.com/-/product/suggestions?for_sku=$id"),
257 fetch_it("http://rest.api.example.com/-/product/reviews?for_sku=$id"),
258 )->then(
259 sub {
260 my ($product, $suggestions, $reviews) = @_;
261 $cv->send({
262 product => $product,
263 suggestions => $suggestions,
264 reviews => $reviews,
265 id => $id
266 })
267 },
268 sub { $cv->croak( 'ERROR' ) }
269 );
270
271 could be rewritten as
272
273 my $id = 12345;
274
275 collect_hash(
276 id => $id,
277 product => fetch_it("http://rest.api.example.com/-/product/$id"),
278 suggestions => fetch_it("http://rest.api.example.com/-/product/suggestions?for_sku=$id"),
279 reviews => fetch_it("http://rest.api.example.com/-/product/reviews?for_sku=$id"),
280 )->then(
281 sub {
282 my %results = @_;
283 $cv->send(\%results);
284 },
285 sub { $cv->croak( 'ERROR' ) }
286 );
287
288 Note that all promise values of the key/value pairs passed to
289 "collect_hash" must return a scalar or nothing, as returning more
290 than one value would mess up the returned hash format. If a promise
291 does return more than one value, "collect_hash" will consider it as
292 having failed.
293
294 If you know that a promise can return more than one value, you can
295 do:
296
297 my $collected = collect_hash(
298 this => $promise_returning_scalar,
299 that => $promise_returning_list->then(sub{ [ @_ ] } ),
300 );
301
303 Promises in General
304 You're Missing the Point of Promises
305 <http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/>
306 Systems Programming at Twitter
307 <http://monkey.org/~marius/talks/twittersystems/>
308 SIP-14 - Futures and Promises <http://docs.scala-
309 lang.org/sips/pending/futures-promises.html>
310 Promises/A+ spec <http://promises-aplus.github.io/promises-spec/>
311 Promises/A spec <http://wiki.commonjs.org/wiki/Promises/A>
312
313 Perl Alternatives
314 Future
315 Mojo::Promise
316 Part of the Mojolicious package.
317
318 Promise::ES6
319 Promise::Tiny
320 AnyEvent::XSPromises
321 Promise::XS
322
324 Stevan Little <stevan.little@iinteractive.com>
325
327 This software is copyright (c) 2020, 2019, 2017, 2014, 2012 by Infinity
328 Interactive, Inc.
329
330 This is free software; you can redistribute it and/or modify it under
331 the same terms as the Perl 5 programming language system itself.
332
333
334
335perl v5.38.0 2023-07-21 Promises(3)