1Promises::Cookbook::GenUtsleerInCtornot(r3i)buted Perl DPorcoummiesnetsa:t:iCoonokbook::GentleIntro(3)
2
3
4

NAME

6       Promises::Cookbook::GentleIntro - All you need to know about Promises
7

VERSION

9       version 0.99
10

All you need to know about Promises

12       If you have every done any async programming, you will be familiar with
13       "callback hell", where one callback calls another, calls another, calls
14       another... Promises give us back a top-to-bottom coding style, making
15       async code easier to manage and understand. It looks like synchronous
16       code, but execution is asynchronous.
17
18       The Promises module is event loop agnostic - it can be used with any
19       event loop. Backends exist for AnyEvent (and thus all the event loops
20       supported by AnyEvent) and Mojo::IOLoop.  But more of this later in
21       "Integration with event loops".
22
23       There are two moving parts:
24
25       Deferred objects
26           Deferred objects provide the interface to a specific async request.
27           They execute some asynchronous action and return a promise.
28
29       Promise objects
30           A promise is like a placeholder for a future result.  The promise
31           will either be resolved in case of success, or rejected in case of
32           failure. Promises can be chained together, and each step in the
33           chain is executed sequentially.
34
35       The easiest way to understand how Deferred and Promise objects work is
36       by example.
37
38   Deferred objects
39       A deferred object is used to signal the success or failure of some
40       async action which can be implemented in the async library of your
41       choice.  For instance:
42
43           use Promises qw(deferred);
44           use AnyEvent::HTTP qw(http_get);
45           use JSON qw(decode_json);
46
47           sub fetch_it {
48               my ($uri) = @_;
49               my $deferred = deferred;
50               http_get $uri => sub {
51                   my ($body, $headers) = @_;
52                   $headers->{Status} == 200
53                       ? $deferred->resolve( decode_json($body) )
54                       : $deferred->reject( $headers->{Reason} )
55               };
56               $deferred->promise;
57           }
58
59       The above code makes an asynchronous "http_get" request to the
60       specified $uri. The result of the request at the time the subroutine
61       returns is like Schrödinger's cat: both dead and alive.  In the future
62       it may succeed or it may fail.
63
64       This sub creates a Promises::Deferred object using "deferred", which is
65       either:
66
67       ·   resolved on success, in which case it returns the request "body",
68           or
69
70       ·   rejected on failure, in which case it returns the reason for
71           failure.
72
73       As a final step, the deferred object returns a Promises::Promise object
74       which represents the future result.
75
76       That's all there is to know about Promises::Deferred.
77
78   Promise objects
79       Promises are a lot like "try"/"catch"/"finally" blocks except that they
80       can be chained together. The most important part of a promise is the
81       "then()" method:
82
83           $promise->then(
84               sub { success! },
85               sub { failure }
86           );
87
88       The "then()" method takes two arguments: a success callback and a
89       failure callback.  But the important part is that it returns a new
90       promise, which is the thing that allows promises to be chained
91       together.
92
93       The simple genius of promises (and I can say that because I didn't
94       invent them) will not be immediately obvious, but bear with me.
95       Promises are very simple, as long as you understand the execution flow:
96
97       Resolving or rejecting a Promise
98
99           use Promises qw(deferred);
100
101           my $deferred = deferred;
102           $deferred->promise->then(
103               sub { say "OK! We received: ".shift(@_)},       # on resolve
104               sub { say "Bah! We failed with: ". shift(@_)}   # on reject
105           );
106
107       What this code does depends on what happens to the $deferred object:
108
109           $deferred->resolve('Yay!');
110           # prints: "OK! We received: Yay!"
111
112           $deferred->reject('Pooh!');
113           # prints "Bah! We failed with: Pooh!"
114
115       A Deferred object can only be resolved or rejected once.  Once it is
116       resolved or rejected, it informs all its promises of the outcome.
117
118       Chaining resolve callbacks
119
120       As mentioned earlier, the "then()" method returns a new promise which
121       will be resolved or rejected in turn. Each "resolve" callback will
122       receive the return value of the previous "resolve" callback:
123
124           deferred
125           ->resolve('red','green')
126           ->promise
127
128           ->then(sub {
129               # @_ contains ('red','green')
130               return ('foo','bar');
131           })
132
133           ->then(sub {
134               # @_ contains ('foo,bar');
135               return 10;
136           })
137
138           ->then( sub {
139               # @_ contains (10)
140           });
141
142       All of these example callbacks have just returned a simple value (or
143       values), so execution has moved from one callback to the next.
144
145       Chaining reject callbacks
146
147       Note that in the above example, in each call to "then()" we specified
148       only a resolved callback, not a rejected callback.  If a promise is
149       resolved or rejected, the action gets passed down the chain until it
150       finds a resolved or rejected handler.  This means that errors can be
151       handled in the appropriate place in the chain:
152
153           my $deferred = deferred;
154
155           $deferred->promise
156           ->then(
157               sub {
158                   my $count = shift();
159                   say "Count: $count";
160                   return $count+1;
161               }
162           )
163           ->then(
164               sub {
165                   my $count = shift();
166                   say "Count: $count";
167                   return $count+1;
168               }
169           )->then(
170               sub {
171                   my $count = shift();
172                   say "Final count: $count";
173                   return $count+1;
174               },
175               sub {
176                   my $reason = shift;
177                   warn "Failed to count: $reason"
178               }
179           );
180
181       If the $deferred object is resolved, it will call each resolved
182       callback in turn:
183
184           $deferred->resolve(5);
185           # prints:
186           #   Count: 5
187           #   Count: 6
188           #   Final count: 7
189
190       If the $deferred object is rejected, however, it will skip all of the
191       steps in the chain until it hits the first rejected callback:
192
193           $deferred->reject('Poor example');
194           # warns:
195           #    "Failed to count: Poor example"
196
197       Important: Event loops do not like fatal exceptions! For this reason
198       the resolved and rejected callbacks are run in "eval" blocks.
199       Exceptions thrown in either type of callback are passed down the chain
200       to the next rejected handler.  If there are no more rejected handlers,
201       then the error is silently swallowed.
202
203       Throwing and handling exceptions
204
205       While you can signal success or failure by calling "resolve()" or
206       "reject()" on the $deferred object, you can also signal success or
207       failure in each step of the promises chain.
208
209       ·   Resolved callbacks are like "try" blocks: they can either execute
210           some code successfully or throw an exception.
211
212       ·   Rejected callbacks are like "catch" blocks: they can either handle
213           the exception or rethrow it.
214
215           $deferred = deferred;
216
217           $deferred->promise
218           ->then(
219               sub {
220                   my $count = shift;
221                   if ( $count > 100 ) {
222                       die "Count too high!"
223                   }
224                   return $count
225               }
226           )->then(
227               sub {
228                   say "The count is OK. Continuing";
229                   return @_
230               },
231               sub {
232                   my $error = shift;
233                   warn "We have a problem: $error";
234                   die $error;
235               }
236           )
237           )->then(
238               undef,  # no resolved handler
239               sub {
240                   return 1;
241               }
242           )-> then(
243               sub {
244                   my $count = shift;
245                   say "Got count: $count";
246               }
247           )
248
249       There are a few ways this code can execute. We can resolve the
250       $deferred object with a reasonable count:
251
252           $deferred->resolve(5);
253           # prints:
254           #   The count is OK. Continuing
255           #   Got count: 5
256
257           $defer
258
259       If we reject the $deferred object, the first rejected handler is
260       called.  It warns, then rethrows the exception with "die" which calls
261       the next rejected handler.  This handler resolves the exception (that
262       is, it doesn't call "die") and returns a value which gets passed to the
263       next resolved handler:
264
265           $deferred->reject('For example purposes')
266           # warns:
267           #    We have a problem: For example purposes
268           # prints:
269           #    Got count: 1
270
271       Finally, if we resolve the $deferred object with a too large count, the
272       first resolved handler throws an exception, which calls the next
273       rejected handler:
274
275           $deferred->resolve(1000);
276           # warns:
277           #    We have a problem: Count too high!
278           # prints:
279           #    Got count: 1
280
281       "catch()"
282
283       In the above example, we called "then()" with "undef" instead of a
284       resolved callback. This could be rewritten to look a bit cleaner using
285       the "catch()" method, which takes just a rejected callback.
286
287           # these two lines are equivalent:
288           $promise->then( undef, sub { rejected cb} )
289           $promise->catch( sub { rejected cb } )
290
291       "finally()"
292
293       Any "try"/"catch" implementation has a "finally" block, which can be
294       used to clean up resources regardless of whether the code in the "try"
295       block succeeded or failed. Promises offer this functionality too.
296
297       The "finally()" method accepts a single callback which is called
298       regardless of whether the previous step was resolved or rejected. The
299       return value (or any exception thrown in the callback) are thrown away,
300       and the chain continues as if it were not there:
301
302           $deferred = deferred;
303           $deferred->promise
304           ->then(
305               sub {
306                   my $count = shift;
307                   if ($count > 10) { die "Count too high"}
308                   return $count
309               }
310           )->finally(
311               sub { say "Finally got: ".shift(@_) }
312           )->then(
313               sub { say "OK: ". shift(@_)   },
314               sub { say "Bah!: ". shift(@_) }
315           );
316
317       If we resolve the $deferred object with a good count, we see:
318
319           $d->resolve(5);
320           # prints:
321           #   Finally got: 5
322           #   OK: 5
323
324       With a high count we get:
325
326           $d->resolve(20);
327           # prints:
328           #   Finally got: Count to high
329           #   Bah: 20
330
331       Chaining async callbacks
332
333       This is where the magic starts: each resolved/rejected handler can not
334       only return a value (or values), it can also return a new Promise.
335       Remember that a Promise represents a future value, which means that
336       execution of the chain will stop until the new Promise has been either
337       resolved or rejected!
338
339       For instance, we could write the following code using the "fetch_it()"
340       sub (see  "Deferred objects") which returns a promise:
341
342           fetch_it('http://domain.com/user/123')
343           ->then(
344               sub {
345                   my $user = shift;
346                   say "User name: ".$user->{name};
347                   say "Fetching total comments";
348                   return fetch_id($user->{total_comments_url});
349               }
350           )->then(
351               sub {
352                   my $total = shift;
353                   say "User has left $total comments"
354               }
355           )
356           ->catch(
357               sub {
358                   warn @_
359               }
360           );
361
362       This code sends an asynchronous request to get the page for user 123
363       and returns a promise. Once the promise is resolved, it sends an
364       asynchronous request to get the total comments for that user and again
365       returns a promise.  Once the second promise is resolved, it prints out
366       the total number of comments. If either promise were to be rejected, it
367       would skip down the chain looking for the first rejected handler and
368       execute that.
369
370       This is organised to look like synchronous code.  Each step is executed
371       sequentially, it is easy to read and easy to understand, but it works
372       asynchronously.  While we are waiting for a response from "domain.com"
373       (while our promise remains unfulfilled), the event loop can happily
374       continue running code elsewhere in the application.
375
376       In fact, it's not just Promises::Promise objects that can be returned,
377       it can be any object that is ``thenable'' (ie it has a "then()"
378       method). So if you want to integrate your Promises code with a library
379       which is using Future objects, you should be able to do it.
380
381       Running async requests in parallel
382
383       Sometimes order doesn't matter: perhaps we want to retrieve several web
384       pages at the same time.  For that we can use the "collect" helper:
385
386           use Promises qw(collect);
387
388           collect(
389               fetch_it('http://rest.api.example.com/-/product/12345'),
390               fetch_it('http://rest.api.example.com/-/product/suggestions?for_sku=12345'),
391               fetch_it('http://rest.api.example.com/-/product/reviews?for_sku=12345'),
392           )->then(
393               sub {
394                   my ($product, $suggestions, $reviews) = @_;
395                   # do something with these values
396               },
397               sub { warn @_ }
398           );
399
400       "collect()" accepts a list of promises and returns a new promise (which
401       we'll call $p for clarification purposes.  When all of its promises
402       have been resolved, it resolves $p with the values returned by every
403       promise, in the same order as they were passed in to "collect()".
404
405       Note: Each promise can return multiple values, so $product,
406       $suggestions and $reviews in the example above will all be array refs.
407
408       If any of the passed in promises is rejected, then $p will also be
409       rejected with the reason for the failure.  $p can only be rejected
410       once, so we wil only find out about the first failure.
411
412   Integration with event loops
413       In order to run asynchronous code, you need to run some event loop.
414       That can be as simple as using "CONDITION VARIABLES" in AnyEvent to run
415       the event loop just until a particular condition is met:
416
417           use AnyEvent;
418
419           my $cv = AnyEvent->condvar;
420           collect(
421               fetch_it('http://rest.api.example.com/-/product/12345'),
422               fetch_it('http://rest.api.example.com/-/product/suggestions?for_sku=12345'),
423               fetch_it('http://rest.api.example.com/-/product/reviews?for_sku=12345'),
424           )->then(
425               sub {
426                   my ($product, $suggestions, $reviews) = @_;
427                   $cv->send({
428                       product     => $product->[0],
429                       suggestions => $suggestions->[0],
430                       reviews     => $reviews->[0],
431                   })
432               },
433               sub { $cv->croak( 'ERROR' ) }
434           );
435
436           # wait for $cv->send or $cv->croak
437           my $results = $cv->recv;
438
439       More usually though, a whole application is intended to be
440       asynchronous, in which case the event loop just runs continuously.
441       Normally you would only need to use $cv's or the equivalent at the
442       point where your application uses a specific async library, as
443       explained in "Deferred objects". The rest of your code can deal purely
444       with Promises.
445
446       Event loop specific backends
447
448       The resolved and rejected callbacks should be run by the event loop,
449       rather than having one callback call the next, which calls the next
450       etc.
451
452       In other words, if a promise is resolved, it doesn't call the resolved
453       callback directly. Instead it adds it to the event loop's queue, then
454       returns immediately. The next time the event loop checks its queue,
455       it'll find the callback in the queue and will call it.
456
457       By default, Promises is event loop agnostic, which means that it
458       doesn't know which event loop to use and so each callback ends up
459       calling the next, etc.  If you're writing Promises-based modules for
460       CPAN, then your code should also be event loop agnostic, in which case
461       you want to use Promises like this:
462
463           use Promises qw(deferred collect);
464
465       However, if you are an end user, then you should specify which event
466       loop you are using at the start of your application:
467
468           use Promises backend => ['AnyEvent']; # or "EV" or "Mojo"
469
470       You only need to specify the backend once - any code in the application
471       which uses Promises will automatically use the specified backend.
472
473   Recursing safely with with "done()"
474       One of the cool things about working with promises is that the return
475       value gets passed down the chain as if we the code were synchronous.
476       However that is not always what we want.
477
478       Imagine that we want to process every line in a file, which could be
479       millions of lines. We don't care about the results from each line, all
480       we care about is whether the whole file was processed successfully, or
481       whether something failed.
482
483       In sync code we'd write something like this:
484
485           sub process_file {
486               my $fh = shift;
487               while (my $line = <$fh>) {
488                   process_line($line)
489                       || die "Failed"
490               }
491           }
492
493       Now imagine that "process_line()" runs asynchronously and returns a
494       promise.  By the time it returns, it probably hasn't executed anything
495       yet.  We can't go ahead and read the next line of the file otherwise we
496       could generate a billion promises before any of them has had time to
497       execute.
498
499       Instead, we need to wait for "process_line()" to complete and only then
500       move on to reading the next line.  We could do this as follows:
501
502           # WARNING: EXAMPLE OF INCORRECT CODE #
503
504           use Promises qw(deferred);
505
506           sub process_file {
507               my $fh        = shift;
508               my $deferred  = deferred;
509               my $processor = sub {
510                   my $line = <$fh>;
511                   unless (defined $line) {
512                       # we're done
513                       return $deferred->resolve;
514                   }
515                   process_line($line)->then(
516
517                       # on success, call $processor again
518                       __SUB__,
519
520                       # on failure:
521                       sub {
522                           return $deferred->reject("Failed")
523                       }
524                   )
525               }
526
527               # start the loop
528               $processor->();
529
530               return $deferred->promise
531           }
532
533       This code has two stack problems. The first is that, every time we
534       process a line, we recurse into the current "__SUB__" from the current
535       sub.  This problem is solved by specifying an "Event loop specific
536       backend" somewhere in our application, which we discussed above.
537
538       The second problem is that every time we recurse into the current
539       "__SUB__" we're waiting for the return value. Other languages use the
540       Tail Call optimization <http://en.wikipedia.org/wiki/Tail_call> to keep
541       the return stack flat, but we don't have this option.
542
543       Instead, we have the "done()" method which, like "then()", accepts a
544       resolved callback and a rejected callback. But it differs from "then()"
545       in two ways:
546
547       ·   It doesn't return a promise, which means that the chain ends with
548           the "done()" step.
549
550       ·   Callbacks are not run in an "eval" block, so calling "die()" will
551           throw a fatal exception. (Most event loops, however will catch the
552           exception, warn, and continue running.)
553
554       The code can be rewritten using "done()" instead of "then()" and an
555       event loop specific backend, and it will happily process millions of
556       lines without memory leaks or stack oveflows:
557
558           use Promises backend => ['EV'], 'deferred';
559
560           sub process_file {
561               my $fh        = shift;
562               my $deferred  = deferred;
563               my $processor = sub {
564                   my $line = <$fh>;
565                   unless (defined $line) {
566                       # we're done
567                       return $deferred->resolve;
568                   }
569                   #### USE done() TO END THE CHAIN ####
570                   process_line($line)->done(
571
572                       # on success, call $processor again
573                       __SUB__,
574
575                       # on failure:
576                       sub {
577                           return $deferred->reject("Failed")
578                       }
579                   )
580               }
581
582               # start the loop
583               $processor->();
584
585               return $deferred->promise
586           }
587

AUTHOR

589       Stevan Little <stevan.little@iinteractive.com>
590
592       This software is copyright (c) 2017, 2014, 2012 by Infinity
593       Interactive, Inc..
594
595       This is free software; you can redistribute it and/or modify it under
596       the same terms as the Perl 5 programming language system itself.
597
598
599
600perl v5.28.0                      2017-10-29Promises::Cookbook::GentleIntro(3)
Impressum