1Future::AsyncAwait(3) User Contributed Perl DocumentationFuture::AsyncAwait(3)
2
3
4

NAME

6       "Future::AsyncAwait" - deferred subroutine syntax for futures
7

SYNOPSIS

9          use v5.14;
10          use Future::AsyncAwait;
11
12          async sub do_a_thing
13          {
14             my $first = await do_first_thing();
15
16             my $second = await do_second_thing();
17
18             return combine_things( $first, $second );
19          }
20
21          do_a_thing()->get;
22

DESCRIPTION

24       This module provides syntax for deferring and resuming subroutines
25       while waiting for Futures to complete. This syntax aims to make code
26       that performs asynchronous operations using futures look neater and
27       more expressive than simply using "then" chaining and other techniques
28       on the futures themselves. It is also a similar syntax used by a number
29       of other languages; notably C# 5, EcmaScript 6, Python 3, Dart, Rust,
30       C++20.
31
32       This module is still under active development. While it now seems
33       relatively stable enough for most use-cases and has received a lot of
34       "battle-testing" in a wide variety of scenarios, there may still be the
35       occasional case of memory leak left in it, especially if still-pending
36       futures are abandoned.
37
38       The new syntax takes the form of two new keywords, "async" and "await".
39
40   "async"
41       The "async" keyword should appear just before the "sub" keyword that
42       declares a new function. When present, this marks that the function
43       performs its work in a potentially asynchronous fashion. This has two
44       effects: it permits the body of the function to use the "await"
45       expression, and it wraps the return value of the function in a Future
46       instance.
47
48          async sub myfunc
49          {
50             return 123;
51          }
52
53          my $f = myfunc();
54          my $result = $f->get;
55
56       As well as named function declarations it is also supported on
57       anonymous function expressions.
58
59          my $code = async sub { return 456 };
60          my $f = $code->();
61          my $result = $f->get;
62
63       This "async"-declared function always returns a "Future" instance when
64       invoked. The returned future instance will eventually complete when the
65       function returns, either by the "return" keyword or by falling off the
66       end; the result of the future will be the return value from the
67       function's code.  Alternatively, if the function body throws an
68       exception, this will cause the returned future to fail.
69
70       If the final expression in the body of the function returns a "Future",
71       don't forget to "await" it rather than simply returning it as it is, or
72       else this return value will become double-wrapped - almost certainly
73       not what you wanted.
74
75          async sub otherfunc { ... }
76
77          async sub myfunc
78          {
79             ...
80             return await otherfunc();
81          }
82
83   "await"
84       The "await" keyword forms an expression which takes a "Future" instance
85       as an operand and yields the eventual result of it. Superficially it
86       can be thought of similar to invoking the "get" method on the future.
87
88          my $result = await $f;
89
90          my $result = $f->get;
91
92       However, the key difference (and indeed the entire reason for being a
93       new syntax keyword) is the behaviour when the future is still pending
94       and is not yet complete. Whereas the simple "get" method would block
95       until the future is complete, the "await" keyword causes its entire
96       containing function to become suspended, making it return a new
97       (pending) future instance. It waits in this state until the future it
98       was waiting on completes, at which point it wakes up and resumes
99       execution from the point of the "await" expression. When the now-
100       resumed function eventually finishes (either by returning a value or
101       throwing an exception), this value is set as the result of the future
102       it had returned earlier.
103
104       "await" provides scalar context to its controlling expression.
105
106          async sub func {
107             # this function is invoked in scalar context
108          }
109
110          await func();
111
112       Because the "await" keyword may cause its containing function to
113       suspend early, returning a pending future instance, it is only allowed
114       inside "async"-marked subs.
115
116       The converse is not true; just because a function is marked as "async"
117       does not require it to make use of the "await" expression. It is still
118       useful to turn the result of that function into a future, entirely
119       without "await"ing on any itself.
120
121       Any function that doesn't actually await anything, and just returns
122       immediate futures can be neatened by this module too.
123
124       Instead of writing
125
126          sub imm
127          {
128             ...
129             return Future->done( @result );
130          }
131
132       you can now simply write
133
134          async sub imm
135          {
136             ...
137             return @result;
138          }
139
140       with the added side-benefit that any exceptions thrown by the elided
141       code will be turned into an immediate-failed "Future" rather than
142       making the call itself propagate the exception, which is usually what
143       you wanted when dealing with futures.
144
145   await (toplevel)
146       Since version 0.47.
147
148       An "await" expression is also permitted directly in the main script at
149       toplevel, outside of "async sub". This is implemented by simply
150       invoking the "get" method on the future value. Thus, the following two
151       lines are directly equivalent:
152
153          await afunc();
154          afunc()->get;
155
156       This is provided as a syntax convenience for unit tests, toplevel
157       scripts, and so on. It allows code to be written in a style that can be
158       easily moved into an "async sub", and avoids encouraging "bad habits"
159       of invoking the "get" method directly.
160
161   "CANCEL"
162       Experimental. Since version 0.44.
163
164       The "CANCEL" keyword declares a block of code which will be run in the
165       event that the future returned by the "async sub" is cancelled.
166
167          async sub f
168          {
169             CANCEL { warn "This task was cancelled"; }
170
171             await ...
172          }
173
174          f()->cancel;
175
176       A "CANCEL" block is a self-contained syntax element, similar to perl
177       constructions like "BEGIN", and does not need a terminating semicolon.
178
179       When a "CANCEL" block is encountered during execution of the "async
180       sub", the code in its block is stored for the case that the returned
181       future is cancelled. Each will take effect as it is executed, possibly
182       multiple times if it appears inside a loop, or not at all if it appears
183       conditionally in a branch that was not executed.
184
185          async sub g
186          {
187             if(0) {
188                CANCEL { warn "This does not happen"; }
189             }
190
191             foreach my $x ( 1..3 ) {
192                CANCEL { warn "This happens for x=$x"; }
193             }
194
195             await ...
196          }
197
198          g()->cancel;
199
200       "CANCEL" blocks are only invoked if a still-pending future is
201       cancelled. They are discarded without being executed if the function
202       finishes; either successfully or if it throws an exception.
203

Experimental Features

205       Some of the features of this module are currently marked as
206       experimental. They will provoke warnings in the "experimental"
207       category, unless silenced.
208
209       You can silence this with "no warnings 'experimental'" but then that
210       will silence every experimental warning, which may hide others
211       unintentionally. For a more fine-grained approach you can instead use
212       the import line for this module to only silence this module's warnings
213       selectively:
214
215          use Future::AsyncAwait qw( :experimental(cancel) );
216
217          use Future::AsyncAwait qw( :experimental );  # all of the above
218

SUPPORTED USES

220       Most cases involving awaiting on still-pending futures should work
221       fine:
222
223          async sub foo
224          {
225             my ( $f ) = @_;
226
227             BEFORE();
228             await $f;
229             AFTER();
230          }
231
232          async sub bar
233          {
234             my ( $f ) = @_;
235
236             return 1 + await( $f ) + 3;
237          }
238
239          async sub splot
240          {
241             while( COND ) {
242                await func();
243             }
244          }
245
246          async sub wibble
247          {
248             if( COND ) {
249                await func();
250             }
251          }
252
253          async sub wobble
254          {
255             foreach my $var ( THINGs ) {
256                await func();
257             }
258          }
259
260          async sub wubble
261          {
262             # on perl 5.35.5 and above
263             foreach my ($k, $v) ( KVTHINGs ) {
264                await func();
265             }
266          }
267
268          async sub quux
269          {
270             my $x = do {
271                await func();
272             };
273          }
274
275          async sub splat
276          {
277             eval {
278                await func();
279             };
280          }
281
282       Plain lexical variables are preserved across an "await" deferral:
283
284          async sub quux
285          {
286             my $message = "Hello, world\n";
287             await func();
288             print $message;
289          }
290
291       On perl versions 5.26 and later "async sub" syntax supports the
292       "signatures" feature if it is enabled:
293
294          use v5.26;
295          use feature 'signatures';
296
297          async sub quart($x, $y)
298          {
299             ...
300          }
301
302       Since version 0.55 any exceptions thrown by signature validation
303       (because of too few or too many arguments being passed) are thrown
304       synchronously, and do not result in a failed Future instance.
305
306   Cancellation
307       Cancelled futures cause a suspended "async sub" to simply stop running.
308
309          async sub fizz
310          {
311             await func();
312             say "This is never reached";
313          }
314
315          my $f = fizz();
316          $f->cancel;
317
318       Cancellation requests can propagate backwards into the future the
319       "async sub" is currently waiting on.
320
321          async sub floof
322          {
323             ...
324             await $f1;
325          }
326
327          my $f2 = floof();
328
329          $f2->cancel;  # $f1 will be cancelled too
330
331       This behaviour is still more experimental than the rest of the logic.
332       The following should be noted:
333
334       •   Cancellation propagation is only implemented on Perl version 5.24
335           and above.  An "async sub" in an earlier perl version will still
336           stop executing if cancelled, but will not propagate the request
337           backwards into the future that the "async sub" is currently waiting
338           on. See "TODO".
339

SUBCLASSING Future

341       By default when an "async sub" returns a result or fails immediately
342       before awaiting, it will return a new completed instance of the Future
343       class. In order to allow code that wishes to use a different class to
344       represent futures the module import method can be passed the name of a
345       class to use instead.
346
347          use Future::AsyncAwait future_class => "Subclass::Of::Future";
348
349          async sub func { ... }
350
351       This has the usual lexically-scoped effect, applying only to "async
352       sub"s defined within the block; others are unaffected.
353
354          use Future::AsyncAwait;
355
356          {
357             use Future::AsyncAwait future_class => "Different::Future";
358             async sub x { ... }
359          }
360
361          async sub y { ... }  # returns a regular Future
362
363       This will only affect immediate results. If the "await" keyword has to
364       suspend the function and create a new pending future, it will do this
365       by using the prototype constructor on the future it itself is waiting
366       on, and the usual subclass-respecting semantics of "new" in Future will
367       remain in effect there. As such it is not usually necessary to use this
368       feature just for wrapping event system modules or other similar
369       situations.
370
371       Such an alternative subclass should implement the API documented by
372       Future::AsyncAwait::Awaitable.
373

WITH OTHER MODULES

375   Syntax::Keyword::Try
376       As of Future::AsyncAwait version 0.10 and Syntax::Keyword::Try version
377       0.07, cross-module integration tests assert that basic "try/catch"
378       blocks inside an "async sub" work correctly, including those that
379       attempt to "return" from inside "try".
380
381          use Future::AsyncAwait;
382          use Syntax::Keyword::Try;
383
384          async sub attempt
385          {
386             try {
387                await func();
388                return "success";
389             }
390             catch {
391                return "failed";
392             }
393          }
394
395       As of Future::AsyncAwait version 0.50, "finally" blocks are invoked
396       even during cancellation.
397
398   Syntax::Keyword::Dynamically
399       As of Future::AsyncAwait version 0.32, cross-module integration tests
400       assert that the "dynamically" correctly works across an "await"
401       boundary.
402
403          use Future::AsyncAwait;
404          use Syntax::Keyword::Dynamically;
405
406          our $var;
407
408          async sub trial
409          {
410             dynamically $var = "value";
411
412             await func();
413
414             say "Var is still $var";
415          }
416
417   Syntax::Keyword::Defer
418       As of Future::AsyncAwait version 0.50, "defer" blocks are invoked even
419       during cancellation.
420
421          use Future::AsyncAwait;
422          use Syntax::Keyword::Defer;
423
424          async sub perhaps
425          {
426             defer { say "Cleaning up now" }
427             await $f1;
428          }
429
430          my $fouter = perhaps();
431          $fouter->cancel;
432
433   Object::Pad
434       As of Future::AsyncAwait version 0.38 and Object::Pad version 0.15,
435       both modules now use XS::Parse::Sublike to parse blocks of code.
436       Because of this the two modules can operate together and allow class
437       methods to be written as async subs which await expressions:
438
439          use Future::AsyncAwait;
440          use Object::Pad;
441
442          class Example
443          {
444             async method perform($block)
445             {
446                say "$self is performing code";
447                await $block->();
448                say "code finished";
449             }
450          }
451
452   Syntax::Keyword::MultiSub
453       As of Future::AsyncAwait version 0.55 and Syntax::Keyword::MultiSub
454       version 0.02 a cross-module integration test asserts that the "multi"
455       modifier can be applied to "async sub".
456
457          use Future::AsyncAwait;
458          use Syntax::Keyword::MultiSub;
459
460          async multi sub f () { return "nothing"; }
461          async multi sub f ($key) { return await get_thing($key); }
462

SEE ALSO

464       •   "Awaiting The Future" - TPC in Amsterdam 2017
465
466           <https://www.youtube.com/watch?v=Xf7rStpNaT0> (slides)
467           <https://docs.google.com/presentation/d/13x5l8Rohv_RjWJ0OTvbsWMXKoNEWREZ4GfKHVykqUvc/edit#slide=id.p>
468

TODO

470       •   Suspend and resume with some consideration for the savestack; i.e.
471           the area used to implement "local" and similar. While in general
472           "local" support has awkward questions about semantics, there are
473           certain situations and cases where internally-implied localisation
474           of variables would still be useful and can be supported without the
475           semantic ambiguities of generic "local".
476
477              our $DEBUG = 0;
478
479              async sub quark
480              {
481                 local $DEBUG = 1;
482                 await func();
483              }
484
485           Since "foreach" loops on non-lexical iterator variables (usually
486           the $_ global variable) effectively imply a "local"-like behaviour,
487           these are also disallowed.
488
489              async sub splurt
490              {
491                 foreach ( LIST ) {
492                    await ...
493                 }
494              }
495
496           Some notes on what makes the problem hard can be found at
497
498           <https://rt.cpan.org/Ticket/Display.html?id=122793>
499
500       •   Currently this module requires perl version 5.16 or later.
501           Additionally, threaded builds of perl earlier than 5.22 are not
502           supported.
503
504           <https://rt.cpan.org/Ticket/Display.html?id=122252>
505
506           <https://rt.cpan.org/Ticket/Display.html?id=124351>
507
508       •   Implement cancel back-propagation for Perl versions earlier than
509           5.24.  Currently this does not work due to some as-yet-unknown
510           effects that installing the back-propagation has, causing future
511           instances to be reclaimed too early.
512
513           <https://rt.cpan.org/Ticket/Display.html?id=129202>
514

KNOWN BUGS

516       This is not a complete list of all known issues, but rather a summary
517       of the most notable ones that currently prevent the module from working
518       correctly in a variety of situations. For a complete list of known
519       bugs, see the RT queue at
520       <https://rt.cpan.org/Dist/Display.html?Name=Future-AsyncAwait>.
521
522       •   "await" inside "map" or "grep" blocks does not work. This is due to
523           the difficulty of detecting the map or grep context from internal
524           perl state at suspend time, sufficient to be able to restore it
525           again when resuming.
526
527           <https://rt.cpan.org/Ticket/Display.html?id=129748>
528
529           As a workaround, consider converting a "map" expression to the
530           equivalent form using "push" onto an accumulator array with a
531           "foreach" loop:
532
533              my @results = map { await func($_) } ITEMS;
534
535           becomes
536
537              my @results;
538              foreach my $item ( ITEMS ) {
539                 push @results, await func($item);
540              }
541
542           with a similar transformation for "grep" expressions.
543
544           Alternatively, consider using the "fmap*" family of functions from
545           Future::Utils to provide a concurrent version of the same code,
546           which can keep multiple items running concurrently:
547
548              use Future::Utils qw( fmap );
549
550              my @results = await fmap { func( shift ) }
551                 foreach    => [ ITEMS ],
552                 concurrent => 5;
553

ACKNOWLEDGEMENTS

555       With thanks to "Zefram", "ilmari" and others from "irc.perl.org/#p5p"
556       for assisting with trickier bits of XS logic.
557
558       Thanks to "genio" for project management and actually reminding me to
559       write some code.
560
561       Thanks to The Perl Foundation for sponsoring me to continue working on
562       the implementation.
563

AUTHOR

565       Paul Evans <leonerd@leonerd.org.uk>
566
567
568
569perl v5.36.0                      2022-11-22             Future::AsyncAwait(3)
Impressum