2020/12/04

2020 Perl Advent Calendar - Day 4

<< First | < Prev | Next >

Now that we've seen some basic introduction on async/await syntax, lets return to exploring and updating the original Futures blog-post series. The next few posts all concerned themselves with error handling, so lets take a look at what that looks like using async/await.

The original day 4 post introduced the ->on_fail method, to apply a callback for handling failures. When using async/await syntax, there is a close relationship between thrown exceptions, and failed futures. Attempting to await on a future that is in a failed state will throw an exception.

We can use any of the usual forms of exception handling to cope with these exceptions. For example, we can use the try/catch syntax from Syntax::Keyword::Try.

use Future::AsyncAwait;
use Syntax::Keyword::Try;

try {
    my $response = await GET("http://my-site-here.com/");
    say "Got a response";
}
catch ($e) {
    print STDERR "It failed: $e\n";
}

The day 5 post then introduced the ->fail method to cause a failure. With an async function, any exceptions thrown by the body of the function become failure results on the returned future instance. The code inside an async sub can simply use regular die to cause a failure.

use feature 'signatures';
use Future::AsyncAwait;

async sub get_page_title($url)
{
    my $response = await GET($url);
    
    if($response->code != 200) {
        die "Unable to fetch page - HTTP status code " . $response->code;
    }

    ...
}

The post on original day 6 then introduced the ->else method on a future instance, which allows failure handling of a future to still be a future. We don't need to do anything special with async/await syntax, because any exception thrown inside an async sub becomes a failed future, and any await on a failed future will re-throw the exception it contains. Thus, failure handling with async/await looks the same as any other sort of exception in synchronous code.

When using Syntax::Keyword::Try we can also put an await expression inside a try block in an async sub, and it behaves entirely as expected. For example, if we wanted to fetch a list of images from an HTML page, but just ignore any failures to fetch and just return an empty list instead.

use feature 'signatures';
use Future::AsyncAwait;
use Syntax::Keyword::Try;

async sub get_page_images($url)
{
   my $response;
   
   try {
       $response = await GET($url);
   }
   catch {
       return ();
   }
   
   ...
}

While there are a number of try/catch modules on CPAN, Syntax::Keyword::Try is specifically known to be compatible in this manner. Both it and Future::AsyncAwait contain a cross-module test script, to ensure that cases like this work. This is one of the reasons why Syntax::Keyword::Try has become my preferred exception-handling module.

<< First | < Prev | Next >

No comments:

Post a Comment