1Future::AsyncAwait(3) User Contributed Perl DocumentationFuture::AsyncAwait(3)
2
3
4
6 "Future::AsyncAwait" - deferred subroutine syntax for futures
7
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
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
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
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
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
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
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
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
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
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
565 Paul Evans <leonerd@leonerd.org.uk>
566
567
568
569perl v5.34.1 2022-04-29 Future::AsyncAwait(3)