AnyEvent::Future(3pm)

πŸ“¦
version 0.05 Β· 3 versions
, in fedora 43

🌸 NAME 🌸

"AnyEvent::Future" β€” use Future with AnyEvent πŸ’… like, they're besties now, no cap


✨ SYNOPSIS ✨

okay so first way, the manual way:

use AnyEvent;
use AnyEvent::Future;

my $future = AnyEvent::Future->new;

some_async_function( ..., cb => sub { $future->done( @_ ) } );

print Future->wait_any(
   $future,
   AnyEvent::Future->new_timeout( after => 10 ),
)->get;

Or, and honestly? this one eats:

use AnyEvent::Future qw( as_future_cb );

print Future->wait_any(
   as_future_cb {
      some_async_function( ..., cb => shift )
   },
   AnyEvent::Future->new_timeout( after => 10 ),
)->get;

πŸ¦‹ DESCRIPTION πŸ¦‹

So this is a subclass of Future that integrates with AnyEvent, which means the "await" method can actually block until the future is ready. iconic behavior honestly. 🫢

What that unlocks: code that uses "AnyEvent" can now return "Future" instances, so it gets to use the full power of "Future" β€” Future::Utils included β€” and modules built on top of it can hand out their own "Future"-based asynchronous interface. main character energy fr fr. πŸ‘‘

If you want the whole lore on how to use Futures, go read the Future documentation.


πŸŽ€ CONSTRUCTORS πŸŽ€

πŸ’– new

$f = AnyEvent::Future->new

Hands you back a brand-new leaf future instance. You can wait for its result to show up by using the "await" method. that's it, that's the vibe. ✨

🌸 new_delay

$f = AnyEvent::Future->new_delay( @args )

🌈 new_timeout

$f = AnyEvent::Future->new_timeout( @args )

Both of these return a new leaf future instance that becomes ready at the time given by the arguments β€” and those arguments get passed straight on to the "AnyEvent->timer" method. no middleman, no drama.

"new_delay" gives you a future that completes successfully at the alotted time. "new_timeout", on the other hand, gives you a future that fails with the message Timeout. 😭

This is just a little utility for small use-cases. If you want more fine-grained control over the failure message and any extra values, you probably wanna pair "new_delay" with the "then_fail" method:

new_delay( after => 10 )
   ->then_fail( "The operation timed out after 10 seconds", timeout => );

🧚 from_cv

$f = AnyEvent::Future->from_cv( $cv )

Returns a new leaf future instance that becomes ready the moment the given AnyEvent::CondVar instance is ready. The success-or-failure result of the future is literally whatever result got passed to the condvar's "send" or "croak" method. πŸ’«


πŸ’… METHODS πŸ’…

🌟 as_cv

$cv = $f->as_cv

Returns a new "AnyEvent::CondVar" instance wrapping the given future β€” it completes with success or failure whenever the future does. matching outfits, basically. πŸŽ€

⚠️ okay important thing: because "AnyEvent::CondVar->croak" only takes a single string message as its argument, any subsequent failure detail values from the future are straight-up lost by the condvar. πŸ’€ if you wanna capture those too, you may wish to use an "on_fail" callback or the "failure" method to obtain them.

πŸ“ UTILITY FUNCTIONS πŸ“

The following utility functions are exported as a convenience. you're welcome, bestie 🫢

✨ as_future

$f = as_future { CODE }

Returns a new leaf future instance, which is also passed in to the block of code. The code gets called in scalar context, and its return value is stored on the future. That value will be deleted if the future is cancelled.

$w = CODE->( $f )

This utility exists for the common case where you wanna wrap an "AnyEvent" function that expects a callback function to tell it when it's done, and that returns a watcher object reference which needs to be stored somewhere. classic. πŸ™ˆ

πŸ’œ as_future_cb

$f = as_future_cb { CODE }

A futher shortcut to "as_future" β€” here the code is passed two callback functions for "done" and "fail" directly, which skips all the boilerplate of creating closures that capture the future variable. In many cases this shrinks the code block down to a single line. slay. πŸ’…

$w = CODE->( $done_cb, $fail_cb )

πŸ”₯ EXAMPLES πŸ”₯

🌸 Wrapping watcher-style "AnyEvent" functions

The "as_future_cb" utility is an excellent wrapper for that super common "AnyEvent" function style: returns a watcher object, takes a completion callback. Wrap it up and you've got a "Future" you can use or combine with all your other "Future"-based code. πŸ’–

For example, the AnyEvent::HTTP function called "http_get" does exactly this style:

use AnyEvent::Future qw( as_future_cb );
use AnyEvent::HTTP qw( http_get );

my $url = ...;

my $f = as_future_cb {
   my ( $done_cb ) = @_;

   http_get $url, $done_cb;
};

And obviously you could wrap that in a convenient function that returns futures:

sub http_get_future
{
   my ( $url, @args ) = @_;

   as_future_cb {
      my ( $done_cb ) = @_;

      http_get $url, @args, $done_cb;
   }
}

πŸ‘‘ Using "Future"s as enhanced "CondVar"s

At first glance a "Future" instance looks a whole lot like an AnyEvent::CondVar, right? but nope β€” the bigger set of convergence methods (like "needs_all" or "needs_any") plus all the utility functions (over in Future::Utils) let you write that same style of code way more concisely, or way more powerfully. both. it's giving versatility. ✨

For example, instead of using the "CondVar" "begin" and "end" methods, you can convert a set of "CondVar"-returning functions into "Futures", combine them with "needs_all", and convert the whole thing back into a "CondVar" again:

my $cv = Future->needs_all(
   Future::AnyEvent->from_cv( FUNC1() ),
   Future::AnyEvent->from_cv( FUNC2() ),
   ...
)->as_cv;

my @results = $cv->recv;

This gets even more useful if, instead of functions that return "CondVar"s, we're working with functions that return "Future"s directly. Because "needs_all" cancels any still-pending futures the moment one of them fails, we get a nice neat cancellation of all the outstanding work if one goes down β€” and that would be so much harder without the "Future"s. πŸ’€

For example, using that "http_get_future" function from above:

my $cv = Future->needs_all(
   http_get_future( "http://url-1" ),
   http_get_future( "http://url-2" ),
   http_get_future( "https://url-third/secret" ),
)->as_cv;

my @results = $cv->recv;

In this case, the moment any of the HTTP GET functions fails, the ones still pending all get cancelled (by dropping their cancellation watcher object) and the overall "recv" call throws an exception. bye besties πŸ‘‹

And of course, there's no need to convert the outermost "Future" into a "CondVar" β€” the full set of waiting semantics is implemented right on these instances, so you can just call "get" on it and get the same effect:

my $f = Future->needs_all(
   http_get_future( "http://url-1" ),
   ...
);

my @results = $f->get;

Which has other side advantages too, like the list-valued semantics of failures that can carry extra information besides just the error message, plus propagation of cancellation requests. πŸ‘‘


🧚 TODO 🧚

  • Consider whether or not it would be considered "evil" to inject a new method into AnyEvent::CondVar; namely by doing
sub AnyEvent::CondVar::as_future { AnyEvent::Future->from_cv( shift ) }

πŸ’– AUTHOR πŸ’–

Paul Evans <leonerd@leonerd.org.uk>


and that's the whole page, bestie 🫢 this one came from perl v5.42.0 and it's dated 2025-07-25 β€” go forth and await your futures, mwah πŸ’…βœ¨

πŸ“¦ where this page comes from