Futures, Promises, Deferreds
Wikipedia says
that a future is a proxy for an unknown, incomplete computation. While
this definition sufficies as a terse description of my current
understanding of what a future is, I initially encountered the concept
while using jQuery, where it is known as
deferred. Below, when I use
JavaScript examples, deferred and Promise will be mostly synonmous
(deferreds are a jQuery specific thing). When I show Scala examples,
Futures and Promises are different, as I will explain.
First Encountering Deferreds
I first encountered it as a means of addressing callback hell. It was common to see code using nested callbacks:
We can rewrite the code using deferreds like so:
Just this benefit was enough for me to start using them, but at the time I didn’t realize some of the other benefits.
Using Promises
Promises are a construct to generalize the idea of asynchronous
results. For example, let’s say we want to:
- Perform an XHR
- Hide an element with a delay
- Create some element on the page
The rest of the examples will use ES2017 syntax. Using jQuery:
Above, we link different types of events in a general way, by using
deferred. $.ajax natively returns a deferred, which supports the
Promise interface.
We manually create a Promise so that we can use
the Promise interface with $.hide.
We can also do cool things like delay the run of a function using a
Promise by using setTimeout:
Next, when we use promises, we can unify error handling. We take the example before and add some error checking.
Using callbacks, this type of handling would be more convulted, requiring either that you have each calling function handle the exceptions of the callee, or wrap each callback in some sort of composing error handler function. This is much more straightforward.
Arguably the biggest benefit of Promises is that they are easy to
compose. Let’s say we need the results of two XHRs:
or that one request depends on the other:
Some other useful things you can do with promises:
Promises along with generators also enable JavaScript’s await ...
async syntax.
Scala Futures
Although Java has something called a Future, it’s not composable in
the sense above. (Java 8 added CompletableFuture which is more similar
to the Futures we are talking about.)
Scala has one that is composable: scala.concurrent.Future. Creating a
Future is similar to how it is done in JavaScript:
The method definition looks like:
Which roughly means that the Future.apply function or just Future
takes function returning a T, and also implicit executor. There are
a few things to unpack here. First, since Scala is a typed language, we
should declare our types. Here, we have some generic type T. The type
is tied to the return value of the function we pass to the Future
function. For example, a Future { 1 } would be a Future[Int].
Scala has something called implicit values. Although implicits have
many uses, when you see them in a method definition, they usually have
to do with the context of the method. In this case, we have want to have
an ExecutionContext whenever we create a Future this way.
Finally, what is an ExecutionContext? In JavaScript, things are
single-threaded and our asynchronous model is based on events. There’s
jsut a single-thread on which all events are processed as they complete.
On the JVM, although there are event-based APIs like java.nio, the
typical model of asynchronicity is thread-based. The ExecutionContext
describes how you want to run the Future. Typically, you will run
Futures on some sort of thread pool.
One of the most confusing differences for me between JavaScript and
Scala is that Scala has both a Future and a Promise. When I first
learned about Scala futures, I always wondered what the use of a
Promise was. The key insight is that a Future represents a value
which has already begun computation. There is no way to cancel it. It
will attempt to be computed. For this reason, you sometimes need a way
to create a Future without starting the computation. This could be
because you are returning a Future to a caller, but do not want them
to have any control over its execution.
In this example, have some sort of internal worker that we don’t have
control of. It has a schedule function: def schedule(f: => Unit) that
it will try to schedule to run in some way. If we want to return a
Future from out addWork function, a promise will be the best way to
do this.
The idea that a Scala Future is already started is important because the
Future equivalents of other languages, such as C#, are more general in
that they do not start automatically. C# has
Tasks
as part of the standard library, which
do not have to immediately start. The
Scalaz library
provides something analagous to this.
Another big difference between Scala Futures and JavaScript Promises
are how you can apply a function to the results. In JavaScript, you can
do this:
In Scala, it’s a little different. We can’t use the same method to
operate both on Ts and Future[T]s.
In Scala, we can use an alternative syntax called a for-comprehension
that some people may find more readable.
Note that these Scala examples above are different than something like
which delays any work on retrieving result2 until the first future has
completed.
Conclusion
Futures or promises, whatever it is you call them, are an interesting
way to get out of callback hell. What’s more, they are a simple way to
add asyncronicity to code. Although using them at first may be a bit
tricky, experience with them will lead to a clear intuition about how
they work. They’ll come a point where you will even know intuitively how
to implement them. Especially if you are experienced with JavaScript,
after some study, that language’s version should be straightfoward to
implement. A good first step is to think about the semantics of then.
There are two key behaviors:
-
thenmaps over the result of aPromise. -
thenwill unwrap aPromisereturned from the function passed.