2013/12/02

Futures advent day 2

Day 2 - Doing something when a Future completes

Because a Future object represents an operation that is currently in progress or has finished it provides the ideal place to attach logic to ask for further activity to happen when the original has completed. The most immediate way is to pass a piece of code in a sub reference to the on_done method.

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

$f->on_done( sub {
  my ( $response ) = @_;
  print "Got a response\n";
});

In this case, as for many posts yet to come, we are presuming some hypothetical GET function that returns a Future wrapping an HTTP operation in the obvious manner.

If the returned Future is already complete (perhaps because it is a synchronous client that always completes immediately, or because it was served internally from a cache) then the on_done method invokes the code immediately. If not, the code is stored inside the Future object itself for when it eventually gets its result.

<< First | < Prev | Next >

8 comments:

  1. Are future's basically a promise object? (perhaps this'll be covered)

    ReplyDelete
    Replies
    1. Essentially, yes. The basic concept was independently created across a number of languages, and it turns out that two sets of terminology were created to name them.

      There tends to be a slight preference that functional languages prefer the word "Future", as compared imperative preferring "Promise". As such, the two styles sometimes have minor different semantics. Though that's only a convention, not a hard rule. C++ for example creates objects in pairs, in a way a little similar to a pipe() call, in that it has one object that the supplier will put the result into, and a different object that the consumer will read the result from. It calls these two the Future and the Promise.

      Delete
  2. I just watched your YAPC::EU 2014 and was very impressed. I would like to work my way through the advent calendar to get up to speed. I'm trying to get them to run.

    However, I'm don't know much about async (hence the appeal), I just use LWP::UserAgent. So I'm afraid the presumption of a GET_HTTP function "wrapped in the obvious manner" is over my head! Can you point to such a function?

    ReplyDelete
    Replies
    1. Ah sure. For a simple synchronous use-case, you could just wrap the LWP::UserAgent HTTP function in a Future-returning function that just returned an immediate Future. For example:

      sub HTTP_GET
      {
        my ($url) = @_;
        my $response = $ua->get($url);
        return Future->done($response);
      }

      This obviously isn't going to do anything asynchronous, but it will still behave like any other Future-returning HTTP get function, allowing it to play along with any other Future-using code.

      Delete
  3. Great! How about a simple asynchronous case?

    ReplyDelete
  4. Well, obviously the simplest asynchronous Future-based HTTP client is the Net::Async::HTTP one, because that already returns Futures:

    $loop->add( my $http = Net:Async::HTTP->new );

    sub HTTP_GET
    {
      my ($url) = @_;
      return $http->GET($url);
    }

    For other event systems, similar concepts will likely be applicable, perhaps via the various Future wrappings, such as for AnyEvent. This one's a little more awkward mostly due to the glue between AnyEvent::HTTP and Future:

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

    sub HTTP_GET
    {
      my ($url) = @_;
      return as_future_cb {
        my ( $done, $fail ) = @_;

        return http_get $url, sub {
          my ( $data, $headers ) = @_;
          defined $data ? $done->( $data ) : $fail->( $headers->{Reason} );
        };
      };
    }

    ReplyDelete
    Replies
    1. I should probably add that nobody ought to rely on this latter implementation as it doesn't return real HTTP::Response objects, only the plain content string. More work would be required to make a real compatible version.

      Delete
  5. Thanks! The Net::Async::HTTP solution is perfect.

    ReplyDelete