This blog post is adapted from a lightning talk I gave at NetflixOSS, Season 2, Episode 2.
I've noticed that when the word "reactive" is mentioned, it tends not to be associated with any code. One of the things that "reactive" means is "non-blocking" code. "Non blocking" means the idea that you can make a call, and then go on and do something else in your program until you get a notification that something happened.
There are a number of frameworks which handle the notification – the idea that a response may not happen immediately – in different ways. Scala has the option of using a couple of different non-blocking mechanisms, and I'm going to go over how they're used and some interestin wrinkles when they are composed together.
Scala uses scala.concurrent.Future as the basic unit of non-blocking access.
The best way I've found to think of a
Future is a box that will, at some point, contain the thing that you want. The key thing with a
Future is that you never open the box. Trying to force open the box will lead you to blocking and grief. Instead, you put the
Future in another, larger box, typically using the
Here's an example of a
Future that contains a
String. When the
Future completes, then
Console.println is called:
Note that in this case, we're calling the
main method and then… finishing. The string's
Future, provided by the global
ExecutionContext, does the work of calling
Console.println. This is great, because when we give up control over when
someString is going to be there and when
Console.println is going to be called, we let the system manage itself. In constrast, look what happens when we try to force the box open:
In this case, we have to wait – keep a thread twiddling its thumbs – until we get
someString back. We've opened the box, but we've had to commandeer the system's resources to get at it.
Event Based Systems with Akka
When we talk about reactive systems in Scala, we're talking about event driven systems, which typically means Akka. When we want to get a result out of Akka, there are two ways we can do it. We can
tell – fire off a message to an actor:
and then rely on
fooActor to send us back a message:
This has the advantage of being very simple and straightforward.
You also have the option of using
ask, which will generate a
When the actor's
receive method sends back
Foo(id) then the
Future will complete. If you want to go the other way, from a
Future to an Actor, then you can use
tell is usually better than
ask, but there are nuances to Akka message processing. I recommend Three flavours of request-response pattern in Akka and Ask, Tell and Per Request Actors for a more detailed analysis of messages, and see the Akka documentation.
The important caveat is that not all systems are Akka-based. If you're talking to a NoSQL store like Redis or Cassandra, odds are that you are using a non-blocking driver that uses Future directly. Or you may be using Play, which will allow you to pass along a Future (and thereby avoid "opening the box") using
What this means, in practice, is that if you're using a system which is not based around Akka Actors, and you're not using a stream based API such as Iteratees / Reactive Streams, then most of the time you hear about "reactive" and "non-blocking", you're going to be looking at
Future. And you're going to pass that
Future as far along the stack as you can, because you want to avoid opening that box. Which brings us to dependent futures.
Assume a service (in Scala,
trait means roughly the same as
interface in Java) that goes and gets data:
This service returns an
Option is another "wrapper" type, which only has two possible values – for
Option[Foo], you'll get back a
Foo(1) exists, or
Foo(1) wasn't found. Using
Option means that we don't have to have null checks all over the place:
Option.map method, we can safely get at the value only if it exists:
You can see that both
Option work on the same principle: you have a type which contains another type, which you can only get at under certain conditions.
FooService isn't non-blocking. If we assume that there's a non-blocking source of data behind the hood, we can do this:
And now we can do the following:
Now, the interesting thing is that we can't compose a
Future with an
Option. If we had a
Future in a
Future then we can flatten it and get back a single
Future, and if we had an
Option in an
Option we could flatten it and get back a single
Option, but we can't flatten a
Future[Option[T]]. That means we can't do this:
This turns out to be a problem.
So, let's add a couple more services to the mix, following the model of
Assuming that all these services return independent futures, you can do the following:
At the end of the code block, there will be three different futures, with potentially three different threads running on them in parallel. This is the model that you'll see in a number of Future tutorials.
Unfortunately, it doesn't always work out like that. Often times, you'll want something that looks roughly like:
In this example, each service needs the resolution of the previous
Future in order to run. This is not quite as ideal, but it's still better than blocking.
So, how do you do this?
The Obvious Solution
Well, given the constraints, the most immediate way is:
I think we can all agree this is not very fun.
There are various different things we can try to make this better. First, we can try "for comprehensions", a piece of useful syntactic sugar that can do wonderful things with
In this case though, because
Option don't compose, the fallback is nested for comprehensions:
Although, Christopher Hunt points out that you can do this:
Which is much neater.
Let's try something else. Here's Scala Async. Instead of using future.map, you use an
async block, and get the result back immediately:
It turns out this won't compile! The reason why is the
flatMap – the async documentation has a note saying “await must not be used inside a closure nested within an async block”, and so the nested
await call fails to fall within the same
However, there are some simple things you can do, that do work.
The simplest thing you can do is to break your code into very small methods, and break them up:
This helps you avoid nesting, and lets you be very explicit about what types you are working with.
Another option is to go ahead and nest everything, then use this neat trick:
While you can't flatten
Option together, you can take a
Future[Option[T]] where T is also
Future[Option[T]] and flatten those together. Credit goes to Jason Zaugg for this one.
We can also use the loan pattern to pass around blocks, and immediately return in the case where we see
None. In this case, we're returning a Play
Result object, which is
NotFound (404) if anything returns
And this gives us:
If you cannot shortcut the
Option, then you can call
option.fold(fail, win) to process both the failure and success branches.
The ultimate answer is to implement a monad transformer called
OptionT to specifically compose
Option. Francois Garillot has written up a step by step blog post addressing how to implement OptionT in Scala.
I recommend Future goodies and helpers: SafeFuture, TimeoutFuture, CancelableFuture if you want to do more with how
Future exceptions are logged, how
Future may timeout, and even cancelling(!) a
We've shown how Akka and Scala provide non-blocking, asynchronous code using
Future and akka message passing, and how to process
Future based code without calling
await or getting into deeply nested code. Hope this helps!