Scala Futures by example

What is a Future?
A Future is essentially a placeholder object that is created for a result that does not yet exist. Scala Futures are asynchronous and non-blocking (by default) and are thus often handled with callbacks.

Hmm. What about ‘callback hell’?
Scala Futures are monadic in nature and can be combined, composed, sequenced, and executed concurrently. All of this can be accomplished with Scala in a very simple, yet highly composable manner. In this article, we will see this in action.

Let’s now explore the following scenarios:

  • Create/Execute a single Future to perform some computation asynchronously
  • Create/Execute multiple asynchronous computations sequentially
  • Create/Execute multiple asynchronous computations
  • Execute multiple callbacks sequentially

Before we begin, to handling a Scala Future, we will require an ExecutionContext.
For the purpose of this blog we will always use global ExecutionContext.
To do so, simply import the following implicit:

import ExecutionContext.Implicits.global

Note: Executions contexts are beyond the scope of this blog post.

Create a single Future to perform some computation asynchronously

In the example below, we first create a new Future that will concatenate a string. In order to handle this future’s result, we the register a callback using the Future‘s onSuccess method. onSuccess takes a partial function which defines the callback and contains our side-effecting code. In this case, onSuccess will simply print the concatenated string.
Note that onSuccess will be called asynchronously once the Future is completed. If the Future has completed prior to our call to onSuccess, then onSuccess will be invoked immediately or scheduled asynchronously.

// Create a future
val future = Future {
  "My " + "First " + "Future."
}

// Print the result
future onSuccess {
  case _  => println _
}

Note: This code does not handle faliure. To do so, one can similarly use onFailure or onComplete.

Execute multiple asynchronous computations sequentially
With the previous example fresh in our minds, let’s take a moment to consider how one might execute a series of futures in sequence. A first attempt, might be to simply create each subsequent Future within the previous Futures’ onSuccess callback — effectively nesting futures. Although, such a solution does work, it can be messy and it quickly leads us into the dreaded realm of callback hell! Noooooooo!

Fortunately, Scala provides a far better option. Scala Futures are monadic and provide map, flatMap, and filter functions! Because Futures are monads they can be used with Scala’s for comprehensions. Scala for comprehensions are incredibly powerful and capable of taking complex code and transforming into something far more simple and readable.

The code snippet below describes an example of this power. This code creates three Futures within a for comprehension. The first future, a, returns the value 10. The second future, b, takes the result of a as input and returns a new result, a * 2. The third future, c, again takes the result of a as input and it returns, a + 2. Finally, the for comprehension yields a new future which will hold the result c.

The result of resFuture using the side-effecting foreach method. The result printed is 12.

val resFuture = for {
  a <- Future(10)
  b <- Future(a * 2)
  c <- Future(a + 2)
  if c > 10 // you can filter too!
} yield c

resFuture foreach println _

Note the elegant structure of the above code. By using a for comprehension, we do not have to write an ugly nested series of callbacks.

The above code also exhibits a very important characteristic. That is, each Future is run after the previous has completed. This is useful at times, but in this particular case, both b and c could execute concurrently. Let’s see how to do that in the next example.

Execute multiple asynchronous computations concurrently
In this example, we will extend the previous example to ensure b and c are executed concurrently, yet still executed after its dependency on a.

val fa = Future { 10 }
// execute fa first
val future = fa.flatMap { a =>
  // execute fb and fc concurrently
  val fb = Future(a * 2)
  val fc = Future(a + 2)
  for {
    b <- fb
    c <- fc
    if c > 10
  } yield c
}
future foreach println _

Executing futures concurrently within a for comprehension is super easy. Simply create the futures first (see fb and fc. The for comprehension’s yield will execute once all futures complete. That’s it!

Execute multiple callbacks in order after some asynchronous computation
Here we will describe how to execute multiple callbacks in a specified order upon a Future‘s completion. Wait?!?! didn’t we already do this in our second example. Sort of. In the second example, we executed multiple Futures in sequence, then had the opportunity to perform some work once they had all completed. In this example, we will invoke multiple callbacks in order after a single Future completes. To do this, we will use andThen

val resFuture = Future { /* eat pancakes */ } andThen {
  case Failure(e) => // vomit
} andThen {
  case _ => // smile happily
}

resFuture foreach println _

In this example we guarantee that // smile happily always occurs after /* eat pancakes */ succeeds or // vomit. Note, that if one of the chained andThen callbacks throws an exception, that exception is not propragated to the subsequent callbacks. Instead, the subsequent andThen callbacks are given the original value of the future.

You may also like...

Leave a Reply