Sponsoring The Perl Toolchain Summit 2025: Help make this important event another success Learn more

NAME

Promises::Cookbook::ScalaFuturesComparison - A comparison of Scala Futures with Promises

VERSION

version 1.01

DESCRIPTION

Here is the example Scala code, it assumes a function called fetch which when given a URL will return a Future.

def getThumbnail(url: String): Future[Webpage] = {
val promise = new Promise[Webpage]
fetch(url) onSuccess { page =>
fetch(page.imageLinks(0)) onSuccess { p =>
promise.setValue(p)
} onFailure { exc =>
promise.setException(exc)
}
} onFailure { exc =>
promise.setException(exc)
}
promise
}

If we take this and translate this into Perl code using the Mojo::UserAgent library, the fetch function would look like this:

sub fetch {
state $ua = Mojo::UserAgent->new;
my $url = shift;
my $d = deferred;
$ua->get($url => sub {
my ($ua, $tx) = @_;
$d->resolve( $tx );
});
$d->promise;
}

And if we were to take the get_thumbnail function and translate it exactly, we would end up with this:

sub get_thumbnail {
my $url = shift;
my $d = deferred;
fetch( $url )->then(
sub {
my $tx = shift;
fetch( $tx->res->dom->find('img')->[0]->{'src'} )->then(
sub { $d->resolve( $_[0] ) },
sub { $d->reject( $_[0] ) },
)
},
sub { $d->reject( $_[0] ) }
);
$d->promise;
}

Scala Futures have a method called flatMap, which takes a function that given value will return another Future. Here is an example of how the getThumbnail method can be simplified by using it.

def getThumbnail(url: String): Future[Webpage] =
fetch(url) flatMap { page =>
fetch(page.imageLinks(0))
}

But since our then method actually creates a new promise and wraps the callbacks to chain to that promise, we don't need this flatMap combinator and so this, Just Works.

sub get_thumbnail {
my $url = shift;
fetch( $url )->then(
sub {
my $tx = shift;
fetch( $tx->res->dom->find('img')->[0]->{'src'} );
}
);
}

Scala Futures also have a rescue method which can serve as a kind of catch block that potentially will return another Future.

val f = fetch(url) rescue {
case ConnectionFailed =>
fetch(url)
}

Just as with flatMap, since our callbacks are wrapped and chained with a new Promise, we can do a rescue just by using the error callback The Promise returned by fetch will get chained and so this will depend on it.

sub get_thumbnail {
my $url = shift;
fetch( $url )->then(
sub {
my $page = shift;
fetch( $page->image_links->[0] );
},
sub {
given ( $_[0] ) {
when ('connection_failed') {
return fetch( $url );
}
default {
return "failed";
}
}
}
);
}

TODO ... figure out how retry can be generic ...

SEE ALSO

Systems Programming at Twitter - http://monkey.org/~marius/talks/twittersystems/

AUTHOR

Stevan Little <stevan.little@iinteractive.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019, 2017, 2014, 2012 by Infinity Interactive, Inc..

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.