Future...cAwait(3pm)
π Future::AsyncAwait(3pm) π
okay so hi bestie π«Ά this is the manual page for Future::AsyncAwait, and it lives in the User Contributed Perl Documentation (that's the 3pm part, iykyk). sit down, grab a strawberry π, and let's get into it.
β¨ NAME β¨
Future::AsyncAwait β deferred subroutine syntax for futures π
β¨ SYNOPSIS β¨
use v5.14;
use Future::AsyncAwait;
async sub do_a_thing
{
my $first = await do_first_thing();
my $second = await do_second_thing();
return combine_things( $first, $second );
}
do_a_thing()->get;
β¨ DESCRIPTION β¨
so this module gives you syntax for deferring and resuming subroutines while they wait for Futures to complete. the whole vibe is: code that does async stuff with futures should look neater and more expressive than just raw then chaining and whatever other tricks you'd do on the futures themselves. and it's also the same kind of syntax a bunch of other languages use β notably C# 5, EcmaScript 6, Python 3, Dart, Rust, C++20. so you're in very good company honestly π
ngl this module is still under active development. it seems relatively stable enough for most use-cases and it's gotten a LOT of "battle-testing" across a wide variety of scenarios, but there may still be the occasional case of memory leak lurking in it β especially if still-pending futures get abandoned. so like, respectfully, be aware π
the new syntax is two new keywords: async and await.
π "async" π
the async keyword should appear just before the sub keyword that declares a new function. when it's there, it marks that this function performs its work in a potentially asynchronous fashion. that gives you two effects:
- it permits the body of the function to use the
awaitexpression β¨ - it wraps the return value of the function in a Future instance π
async sub myfunc
{
return 123;
}
my $f = myfunc();
my $result = $f->get;
and it's not just for named function declarations β anonymous function expressions get it too π
my $code = async sub { return 456 };
my $f = $code->();
my $result = $f->get;
an async-declared function always returns a Future instance when you invoke it. that returned future instance will eventually complete when the function returns β either via the return keyword or by just falling off the end β and the result of the future will be the return value from the function's code. alternatively, if the function body throws an exception, that causes the returned future to fail.
β οΈ if the final expression in the body of your function returns aFuture, don't forget toawaitit instead of just returning it as-is. otherwise the return value becomes double-wrapped, which is almost certainly not what you wanted bestie π
async sub otherfunc { ... }
async sub myfunc
{
...
return await otherfunc();
}
since version 0.69, this module also supports the async keyword on lexical subroutine declarations, but only when running on Perl version 5.18 or later. heads up: the my keyword has to come first π
use v5.18;
my async sub lexfunc { ... }
my $f = lexfunc(@args);
and since version 0.70, this module supports using the async keyword to declare named subs in other packages π¦
async sub Some::Other::Package::myfunc { ... }
πΈ "await" πΈ
the await keyword forms an expression that takes a Future instance as its operand and yields the eventual result of it. superficially you can think of it like invoking the get method on the future π
my $result = await $f;
my $result = $f->get;
but here's the key difference β and honestly the entire reason this is a whole new syntax keyword β it's about what happens when the future is still pending and not yet complete. the simple get method would just block until the future is complete. the await keyword, though, causes its entire containing function to become suspended, making it return a new (pending) future instance. it waits in that state until the future it was waiting on completes, at which point it wakes up and resumes execution right from the point of the await expression. and then, when the now-resumed function eventually finishes β either by returning a value or by throwing an exception β that value gets set as the result of the future it had returned earlier. it's giving main character energy tbh β¨
await provides scalar context to its controlling expression.
async sub func {
# this function is invoked in scalar context
}
await func();
and because the await keyword may cause its containing function to suspend early and return a pending future instance, it is only allowed inside async-marked subs π«
the converse is not true though! just because a function is marked async does not require it to use the await expression. it's still useful to turn the result of that function into a future entirely without awaiting on anything yourself π
any function that doesn't actually await anything and just returns immediate futures can be neatened up by this module too.
instead of writing:
sub imm
{
...
return Future->done( @result );
}
you can now simply write:
async sub imm
{
...
return @result;
}
with the added side-benefit that any exceptions thrown by the elided code will be turned into an immediate-failed Future, rather than making the call itself propagate the exception β which is usually what you wanted when dealing with futures π«Ά
π§ await (toplevel) π§
since version 0.47.
an await expression is also permitted directly in the main script at toplevel, outside of an async sub. it's implemented by simply invoking the get method on the future value. so these next two lines are directly equivalent, no cap:
await afunc();
afunc()->get;
this exists as a syntax convenience for unit tests, toplevel scripts, and so on πΈ it lets you write code in a style that can be easily moved into an async sub, and it avoids encouraging those "bad habits" of invoking the get method directly π
π
"CANCEL" π
Experimental. Since version 0.44.
the CANCEL keyword declares a block of code which will be run in the event that the future returned by the async sub is cancelled.
async sub f
{
CANCEL { warn "This task was cancelled"; }
await ...
}
f()->cancel;
a CANCEL block is a self-contained syntax element, similar to perl constructions like BEGIN, and it does not need a terminating semicolon. iconic behaviour honestly β¨
when a CANCEL block is encountered during execution of the async sub, the code in its block is stored for the case that the returned future is cancelled. each one will take effect as it is executed β which means it could happen multiple times if it appears inside a loop, or not at all if it appears conditionally in a branch that wasn't executed π
async sub g
{
if(0) {
CANCEL { warn "This does not happen"; }
}
foreach my $x ( 1..3 ) {
CANCEL { warn "This happens for x=$x"; }
}
await ...
}
g()->cancel;
CANCEL blocks are only invoked if a still-pending future is cancelled. they're discarded without being executed if the function finishes β whether it finishes successfully or throws an exception π
π§ͺ Experimental Features π§ͺ
some of the features of this module are currently marked as experimental. they will provoke warnings in the experimental category, unless you silence them.
you can silence this with no warnings 'experimental', but then that silences every experimental warning, which may hide others unintentionally. for a more fine-grained approach, you can instead use the import line for this module to only silence this module's warnings selectively π
use Future::AsyncAwait qw( :experimental(cancel) );
use Future::AsyncAwait qw( :experimental ); # all of the above
π SUPPORTED USES π
most cases involving awaiting on still-pending futures should work fine πΈ
async sub foo
{
my ( $f ) = @_;
BEFORE();
await $f;
AFTER();
}
async sub bar
{
my ( $f ) = @_;
return 1 + await( $f ) + 3;
}
async sub splot
{
while( COND ) {
await func();
}
}
async sub wibble
{
if( COND ) {
await func();
}
}
async sub wobble
{
foreach my $var ( THINGs ) {
await func();
}
}
async sub wubble
{
# on perl 5.35.5 and above
foreach my ($k, $v) ( KVTHINGs ) {
await func();
}
}
async sub quux
{
my $x = do {
await func();
};
}
async sub splat
{
eval {
await func();
};
}
plain lexical variables are preserved across an await deferral π
async sub quux
{
my $message = "Hello, world\n";
await func();
print $message;
}
on perl versions 5.26 and later, async sub syntax supports the signatures feature if it's enabled β¨
use v5.26;
use feature 'signatures';
async sub quart($x, $y)
{
...
}
since version 0.55, any exceptions thrown by signature validation (because of too few or too many arguments being passed) are thrown synchronously, and do not result in a failed Future instance π«Ά
π Cancellation π
cancelled futures cause a suspended async sub to simply stop running.
async sub fizz
{
await func();
say "This is never reached";
}
my $f = fizz();
$f->cancel;
cancellation requests can also propagate backwards into the future the async sub is currently waiting on π
async sub floof
{
...
await $f1;
}
my $f2 = floof();
$f2->cancel; # $f1 will be cancelled too
this behaviour is still more experimental than the rest of the logic. so bestie, please note the following:
- Cancellation propagation is only implemented on Perl version 5.24 and above. an
async subon an earlier perl version will still stop executing if cancelled, but it will not propagate the request backwards into the future that theasync subis currently waiting on. see"TODO"π
β¨ SUBCLASSING Future β¨
okay so by default, when an async sub returns a result, or just absolutely faceplants before it even gets to await anything, it hands you back a brand-new, already-completed instance of the Future class. but likeβ¦ what if you want a different class?? bestie, they thought of that π
if your code wants to represent futures with its own special class, you can just pass the name of that class to the import method. iconic.
use Future::AsyncAwait future_class => "Subclass::Of::Future";
async sub func { ... }
and this has the usual lexically-scoped behaviour, meaning it only applies to async subs defined inside the block. everybody else? untouched. living their lives. π¦
use Future::AsyncAwait;
{
use Future::AsyncAwait future_class => "Different::Future";
async sub x { ... }
}
async sub y { ... } # returns a regular Future
heads up though!! this only affects immediate results. if the await keyword actually has to suspend the function and create a new pending future, it does that by using the prototype constructor on the future it's itself waiting on, and the usual subclass-respecting semantics of new in Future stay in effect there. so basically it's usually not necessary to reach for this feature just for wrapping event system modules or other similar situations. no cap. π
any alternative subclass should implement the API documented by Future::AsyncAwait::Awaitable.
π WITH OTHER MODULES π
Syntax::Keyword::Try
as of Future::AsyncAwait version 0.10 and Syntax::Keyword::Try version 0.07, the cross-module integration tests assert that basic try/catch blocks inside an async sub work correctly β including the ones that try to return from inside try. slay. β¨
use Future::AsyncAwait;
use Syntax::Keyword::Try;
async sub attempt
{
try {
await func();
return "success";
}
catch {
return "failed";
}
}
and as of Future::AsyncAwait version 0.50, finally blocks get invoked even during cancellation. π
Syntax::Keyword::Dynamically
as of Future::AsyncAwait version 0.32, the cross-module integration tests assert that dynamically correctly works across an await boundary. the way that justβ¦ works ππ
use Future::AsyncAwait;
use Syntax::Keyword::Dynamically;
our $var;
async sub trial
{
dynamically $var = "value";
await func();
say "Var is still $var";
}
Syntax::Keyword::Defer
as of Future::AsyncAwait version 0.50, defer blocks get invoked even during cancellation. periodt. π
use Future::AsyncAwait;
use Syntax::Keyword::Defer;
async sub perhaps
{
defer { say "Cleaning up now" }
await $f1;
}
my $fouter = perhaps();
$fouter->cancel;
Object::Pad
as of Future::AsyncAwait version 0.38 and Object::Pad version 0.15, both modules now use XS::Parse::Sublike to parse blocks of code. because of this, the two modules can operate together and let class methods be written as async subs that await expressions. main character energy, honestly. π
use Future::AsyncAwait;
use Object::Pad;
class Example
{
async method perform($block)
{
say "$self is performing code";
await $block->();
say "code finished";
}
}
Syntax::Keyword::MultiSub
as of Future::AsyncAwait version 0.55 and Syntax::Keyword::MultiSub version 0.02, a cross-module integration test asserts that the multi modifier can be applied to async sub. obsessed. π«Ά
use Future::AsyncAwait;
use Syntax::Keyword::MultiSub;
async multi sub f () { return "nothing"; }
async multi sub f ($key) { return await get_thing($key); }
π SEE ALSO π
- "Awaiting The Future" - TPC in Amsterdam 2017
<https://www.youtube.com/watch?v=Xf7rStpNaT0> (slides) <https://docs.google.com/presentation/d/13x5l8Rohv_RjWJ0OTvbsWMXKoNEWREZ4GfKHVykqUvc/edit#slide=id.p>
π TODO π
- Suspend and resume with some consideration for the savestack; i.e. the area used to implement
localand similar. While in generallocalsupport has awkward questions about semantics, there are certain situations and cases where internally-implied localisation of variables would still be useful and can be supported without the semantic ambiguities of genericlocal.
our $DEBUG = 0;
async sub quark
{
local $DEBUG = 1;
await func();
}
Since foreach loops on non-lexical iterator variables (usually the $_ global variable) effectively imply a local-like behaviour, these are also disallowed. π«
async sub splurt
{
foreach ( LIST ) {
await ...
}
}
Some notes on what makes the problem hard can be found at
<https://rt.cpan.org/Ticket/Display.html?id=122793>
- Currently this module requires perl version
5.16or later. Additionally, threaded builds of perl earlier than5.22are not supported.
<https://rt.cpan.org/Ticket/Display.html?id=122252>
<https://rt.cpan.org/Ticket/Display.html?id=124351>
- Implement cancel back-propagation for Perl versions earlier than
5.24. Currently this does not work due to some as-yet-unknown effects that installing the back-propagation has, causing future instances to be reclaimed too early.
<https://rt.cpan.org/Ticket/Display.html?id=129202>
π KNOWN BUGS π
ngl bestie, this is not a complete list of all known issues. it's more like a summary of the most notable ones β the ones currently preventing the module from working correctly in a bunch of situations. for a complete list of known bugs, go check the RT queue at <https://rt.cpan.org/Dist/Display.html?Name=Future-AsyncAwait>.
awaitinsidemaporgrepblocks does not work. π this is because it's genuinely hard to detect the map or grep context from internal perl state at suspend time, well enough to be able to restore it again when resuming.
<https://rt.cpan.org/Ticket/Display.html?id=129748>
as a workaround, consider converting a map expression into the equivalent form using push onto an accumulator array with a foreach loop:
my @results = map { await func($_) } ITEMS;
becomes
my @results;
foreach my $item ( ITEMS ) {
push @results, await func($item);
}
with a similar transformation for grep expressions.
alternatively, consider using the fmap* family of functions from Future::Utils to give you a concurrent version of the same code, which can keep multiple items running concurrently:
use Future::Utils qw( fmap );
my @results = await fmap { func( shift ) }
foreach => [ ITEMS ],
concurrent => 5;
- The default arguments array (
@_) is not saved and restored by anawaitcall on perl versions beforev5.24. on those older perls, the value you see in the@_array after an await will not be the same as it was before. π¬
<https://rt.cpan.org/Ticket/Display.html?id=130683>
as a workaround, make sure to unpack the values out of it into regular lexical variables early on, before the the first await. the values of these lexicals will be saved and restored as normal.
async sub f
{
my ($vars, $go, @here) = @_;
# do not make further use of @_ afterwards
await thing();
# $vars, $go, @here are all fine for use
}
π«Ά ACKNOWLEDGEMENTS π«Ά
With thanks to "Zefram", "ilmari" and others from "irc.perl.org/#p5p" for assisting with trickier bits of XS logic. legends, genuinely. π
Thanks to "genio" for project management and actually reminding me to write some code.
Thanks to The Perl Foundation for sponsoring me to continue working on the implementation.
π AUTHOR π
Paul Evans <leonerd@leonerd.org.uk>
and that's a wrap bestie!! this page is from perl v5.42.3, dated 2026-08-20, and it's the Future...cAwait(3pm) manpage β go forth and await things responsibly. mwah π
β¨πΈ
π¦ where this page comes from
- ubuntu stonking: libfuture-asyncawait-perl 0.71-1build1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms