NAME Async::Methods - Namespaced sugar methods for async/await and future/promise based code SYNOPSIS use Mojo::UserAgent; my $ua = Mojo::UserAgent->new; # Normal synchronous code print $ua->get('http://trout.me.uk/')->result->body; # Equivalent code running synchronously atop promises print $ua->get_p('http://trout.me.uk')->then::result->await::body; # Equivalent code within an async subroutine use Mojo::Base -async_await, -signatures; async sub fetch ($url) { await $ua->get_p($url)->then::result->then::body; } print fetch($url)->await::this; DESCRIPTION Async::Methods provides a set of helper methods operating via namespace that make chaining together asynchronous methods easier. This is not at all meant to be a replacement for the "async" and "await" keywords available via Future::AsyncAwait or the "-async_await" flag to Mojo::Base and in fact is largely meant to be used *with* such facilities. Note that in the following code I use $p for example variables but they can be Future or Mojo::Promise objects or (hopefully) objects of any other class that provides a similar interface. Note that methods of each type provided can be called three ways: $obj->the_type::some_method(@args); will call "some_method" on a relevant object, and is effectively simply sugar for the second type, $obj->the_type::_(some_method => @args); which calls the method name given in its first argument (yes, this means that you can't use the first syntax to call a method called "_" but the author of this module strongly suspects that won't be an inconvience in most cases). Thirdly, to match perl's capacity to allow <$obj->$cb(@args)> as a syntax, you can also call: $obj->the_type::_(sub { ... } => @args); $obj->the_type::_($cb => @args); to call that code reference as a method. METHODS start:: my $p = $obj->start::some_method(@args); my $p = $obj->start::_(some_method => @args); my $p = $obj->start::_(sub { ... } => @args); "start::" methods don't do anything special in and of themselves but register the $obj with Async::Methods to allow "catch::" and "else::" to work correctly (see their documentation below for why you might find that useful). Other than the registration part, this is entirely equivalent to my $p = $obj->some_method(@args); then:: my $then_p = $p->then::some_method(@args); my $then_p = $p->then::_(some_method => @args); my $then_p = $p->then::_(sub { ... } => @args); "then::" allows for chaining an additional method call from the return value of the previous promise (assuming it's successful). As such, on its own this is equivalent to my $then_p = $p->then( sub ($obj, @rest) { $obj->some_method(@args, @rest)) } ); Note that "then::" does not require anything special of the promise upon which it's called to provide the base functionality, but *does* need to be called on the result of something rooted in "start::" if you want to be able to chain "else::" or "catch::" from the return value. else:: my $else_p = $p->else::some_method(@args); my $else_p = $p->else::_(some_method => @args); my $else_p = $p->else::_(sub { ... } => @args); "else::" must be called on the result of a "start::" chained to a "then::", and provides a callback if the start::ed method fails, invoked on the *original* invocant. This makes it the "other half" of Async::Methods' support for two-arg "<-"then>>, so: my $else_p = $obj->start::one(@args1) ->then::two(@args2) ->else::three(@args3); is functionally equivalent to: my $else_p = $obj->one(@args1) ->then( sub ($then_obj, @then_rest) { $then_obj->two(@args2, @then_rest) }, sub (@error) { $obj->three(@args3, @error) }, ); which the author hopes explains why you might, on the whole, not really mind being forced to type start::. Note that because "else::" always resolves to the second argument to a two-arg "then" call, it can't be used in isolation. Fortunately, we already provide "catch::" for that, which is documented next. catch:: my $catch_p = $p->catch::some_method(@args); my $catch_p = $p->catch::_(some_method => @args); my $catch_p = $p->catch::_(sub { ... } => @args); "catch::" can be called on the result of either a "start::" call or a "start::" -> "then::" chain, and will catch any/all errors produced up to this point, as opposed to "else::" which catches errors *before* the preceding "then::" call. As such, morally equivalent to: my $catch_p = $obj->start::whatever(...) ->catch(sub ($obj, @error) { $obj->some_method(@args, @error) }); await:: my $ret = $p->await::this; "await::this" is simple generic sugar for (at top level of your code outside of an already-running event loop) spinning the event loop until the promise completes and then either returning the result on success or "die()"ing with the error on failure. For a future, it's equivalent to my $ret = $f->get; but if called on a Mojo::Promise loads Mojo::Promise::Role::Get and uses that to complete the operation, so "await::this" can be called on either and still provides a uniform interface. Assuming you install Mojo::Promise::Role::Get if you need it of course - otherwise you'll get an exception from the relevant "require" call. my $ret = $p->await::some_method(@args); my $ret = $p->await::_(some_method => @args); my $ret = $p->await::_(sub { ... } => @args); "await::" requires absolutely nothing of the promise upon which it's called, and other than the special case of "this" is equivalent to my $ret = $p->then::some_method(@args)->await::this; Hopefully obvious caveat: If you want to await a method called "this" you'll need to call one of my $ret = $p->then::this(@args)->await::this; my $ret = $p->await::_(this => @args); but "this" did not strike the author as a sufficiently common method name to be a deal-breaker in practice. AUTHOR mst - Matt S. Trout (cpan:MSTROUT) CONTRIBUTORS None yet - maybe this software is perfect! (ahahahahahahahahaha) COPYRIGHT Copyright (c) 2020 the Async::Methods "AUTHOR" and "CONTRIBUTORS" as listed above. LICENSE This library is free software and may be distributed under the same terms as perl itself.