2020/12/01

2020 Perl Advent Calendar - Day 1

<< First | < Prev | Next >

A few years ago I wrote an advent calendar series of blog posts about handling asynchronous control flow using Futures in Perl. I've decided it's about time to revisit that and update it with more modern style - namely, using the new async/await syntax provided by Future::AsyncAwait.

On Day 1 we began with a small simple function that didn't actually do anything asynchronous, but simply returned a calculated immediately by using the Future->new->done constructor. Using async/await syntax this is much simpler. In fact, apart from the presence of the async and await keywords here you'd be hard pushed to even realise anything different was going on:

use Future::AsyncAwait;

async sub sum
{
   my @numbers = @_;
   my $total = 0;
   $total += $_ for @numbers;
   return $total;
}

say "The total is " . await sum( 10, 20, 30 );

Already in this small example we can see the beginnings of a pattern - that any function declared as async sub needs to be called as part of an await expression. The await expression will eventually yield the result that the asynchronous function returned.

Of course, there's no reason for this particular code to be asynchronous, so by Day 2 I had invented a hypothetical GET function which took a URL and returned a future that will eventually resolve with some sort of HTTP response - we can imagine that being provided by some sort of asynchronous HTTP library. That example showed the on_done method on the future instance.

The await keyword simply suspends the function it is currently executing in, so we can come back later when a result arrives. This allows us to write an example where we wait for an HTTP request/response cycle to complete, and then perform some more work afterwards:

use Future::AsyncAwait;

my $response = await GET("http://my-site-here.com/");

print "Got a response\n";

In the original series we had to get to Day 3 to introduce the then method, which allows a sequence of operations to be performed. No such complication is required with async/await, because we can simply await a second time after the first one, to execute a whole sequence of operations. For example, now we can perform a second request based on some analysis of the result of the first.

use Future::AsyncAwait;

my $first_response = await GET("http://my-site-here.com/first");

my $path = path_from_response($first_response);
my $second_response = await GET("http://my-site-here.com/$path);

printf "Second response contains %d bytes\n", length $second_response->body;

If this is your first experience of futures, asynchronous code flow, and async/await syntax, it may not be immediately obvious currently why you want to do this. I hope to motivate all of this, and more, over the following posts...

<< First | < Prev | Next >

No comments:

Post a Comment