NAME
Mojo::AsyncAwait - An Async/Await implementation for Mojolicious
SYNOPSIS
use Mojolicious::Lite -signatures;
use Mojo::AsyncAwait;
get '/' => async sub ($c) {
my $mojo = await $c->ua->get_p('
https://mojolicious.org');
my $cpan = await $c->ua->get_p('
https://metacpan.org');
$c->render(json => {
mojo => $mojo->result->code,
cpan => $cpan->result->code
});
};
app->start;
DESCRIPTION
Async/await is a language-independent pattern that allows nonblocking
asynchronous code to be structured simliarly to blocking code. This is
done by allowing execution to be suspended by the await keyword and
returning once the promise passed to await has been fulfilled.
This pattern simplies the use of both promises and nonblocking code in
general and is therefore a very exciting development for writing
asynchronous systems.
If you are going to use this module to create async controllers actions
in Mojolicious applications (as seen in the "SYNOPSIS") before
Mojolicious version 8.28, you are highly encouraged to also use
Mojolicious::Plugin::PromiseActions in order to properly handle
exceptions in your action.
GOALS
The primary goal of this module is to provide a useful Async/Await
implementation for users of the Mojolicious ecosystem. It is for this
reason that Mojo::Promise is used when new promises are created.
Because this is the primary goal, the intention is for it to remain
useful even as other goals are considered.
Secondarily, it is intended to be a testbed for early implementations
of Async/Await in the Perl 5 language. It is for this reason that the
implementation details are intended to be replaceable. The result
should hopefully still be backwards compatible, mostly because the
interface is so simple. After all, it is just two keywords.
Of course, I always intend as much as possible that Mojolicious-focused
code is as useful as practically possible for the broader Perl 5
ecosystem. It is for this reason that while this module returns
Mojo::Promises, it can accept any then-able (read: promise) which
conforms enough to the Promises/A+ standard. The Promises/A+ standard
is intended to increase the interoperability of promises, and while
that line becomes more gray in Perl 5 where we don't have a single
ioloop implementation, we try our best.
Finally the third goal is to improve the mobility of the knowledge of
this pattern between languages. Users of Javascript probably are
already familiar with this patthern; when coming to Perl 5 they will
want to continue to use it. Likewise, as Perl 5 users take on new
languages, if they are familiar with common patterns in their new
language, they will have an easier time learning. Having a useable
Async/Await library in Perl 5 is key to keeping Perl 5 relevent in
moderning coding.
BACKENDS
This module actually does very little on its own, it simply loads and
imports backend implementations of Async/Await. The reason to use this
module really would be to use current default implementation without
regards to what that implementation is nor how it works.
When it is loaded, the MOJO_ASYNCAWAIT_BACKEND is checked, if not set
then the current default is used.
# From environment
BEGIN{ $ENV{MOJO_ASYNCAWAIT_BACKEND} = '+CoolBackend' }
use Mojo::AsyncAwait;
# Currently provided default
use Mojo::AsyncAwait;
The backend is specified either as a fully qualified module name, e.g.
Mojo::AsyncAwait::Backend::CoolBackend or using the + as a shortcut for
Mojo::AsyncAwait::Backend::, e.g. +CoolBackend which would mean exactly
the same as the former.
CAVEATS
First and foremost, this is all a little bit crazy. Please consider
carefully before using this code in production.
While many languages have async/await as a core language feature,
currently in Perl we must rely on modules that provide the mechanism of
suspending and resuming execution.
The default implementation relies on Coro which does some very magical
things to the Perl interpreter. Other less magical implementations are
in the works however none are available yet. As available
implementations change or stabilize, that default may be changed.
Backend implementations may be added or even be spun off. If your
application depends on the backend implementation, you may import it
manually or use the described mechanisms to load it. In that case you
should be sure to add the backend to your dependency list in case it is
spun off in the future.
Also note that while a Coro-based implementation need not rely on
"await" being called directly from an "async" function, it is currently
prohibitied because it is likely that other/future implementations will
rely on that behavior and thus it should not be relied upon.
KEYWORDS
Regardless of backend, Mojo::AsyncAwait provides two keywords (i.e.
functions), both exported by default. Depending on backend, their exact
behavior might change slightly, however, implementers should attempt to
follow the api described here as closely as possible.
Some backends may allow additional options to be passed to the
keywords; those options should be kept minimal and if possible follow
the conventions described in Mojo::AsyncAwait::Backend::Coro. This
generic document will not describe those additional options.
async
my $sub = async sub { ... };
The async keyword wraps a subroutine as an asynchronous subroutine
which is able to be suspended via "await". The return value(s) of the
subroutine, when called, will be wrapped in a Mojo::Promise.
The async keyword must be called with a subroutine reference, which
will be the body of the async subroutine.
Note that the returned subroutine reference is not invoked for you. If
you want to immediately invoke it, you need to so manually.
my $promise = async(sub{ ... })->();
If called with a preceding name, the subroutine will be installed into
the current package with that name.
async installed_sub => sub { ... };
installed_sub();
Unlike the case of an anonymous wrapped async subroutine reference
described above, if the subroutine is installed, nothing is returned.
await
my $tx = await Mojo::UserAgent->new->get_p('
https://mojolicious.org');
my @results = await (async sub { ...; return @async_results })->();
The await keyword suspends execution of an async sub until a promise is
fulfilled, returning the promise's results. In list context all promise
results are returned. For ease of use, in scalar context the first
promise result is returned and the remainder are discarded.
If the value passed to await is not a promise (defined as having a then
method), it will be wrapped in a Mojo::Promise for consistency. This is
mostly inconsequential to the user.
Note that await can only take one promise as an argument. If you wanted
to await multiple promises you probably want "all" in Mojo::Promise or
less likely "race" in Mojo::Promise.
my $results = await Mojo::Promise->all(@promises);
AUTHORS
Joel Berger <
[email protected]>
Marcus Ramberg <
[email protected]>
CONTRIBUTORS
Sebastian Riedel <
[email protected]>
ADDITIONAL THANKS
Matt S Trout (mst)
Paul Evans (LeoNerd)
John Susek
COPYRIGHT AND LICENSE
Copyright (C) 2018, "AUTHORS" and "CONTRIBUTORS".
This program is free software, you can redistribute it and/or modify it
under the terms of the Artistic License version 2.0.
SEE ALSO
Mojo::Promise
Mojo::IOLoop
Mojolicious::Plugin::PromiseActions
MDN Async/Await
<
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function>
Coro::State
Future::AsyncAwait
PerlX::AsyncAwait