Scala Futures 101


Before we can dive into Akka Streams, we have to better understand concurrency in the Java and Scala world.

Scala, as an JVM language, heavily depends on Java API. The same applies to scala.concurrent package. For example, scala.concurrent.ExecutionContext, is very similar to java.util.concurrent.Executor, and is able to any java.lang.Runnable that is passed into its execute-method, and works great with java.util.concurrent.ForkJoinPool directly. That’s where we are going to start.


java.lang.Runnable is at the core of Java threading. It defines a single method, run, which is going to be executed in the thread. A Thread takes a Runnable instance. To implement threading code, we can do this:

val helloWorld = new Thread(new Runnable {
    def run() {
        println("hello world")

This way we simply define the thread. To execute it, we have to call start method:

@ helloWorld.start()
hello world


Let’s take a look at the ExecutionContext trait. ExecutionContext is defined as a trait in scala.concurrent.ExecutionContext:

trait ExecutionContext {
  def execute(runnable: Runnable): Unit
  def reportFailure(cause: Throwable): Unit

Running single Thread is a rare case. More commonly we need dozens or hundreds, and we want to control the lifecycle of particular set of Threads. That’s where ExecutionContext comes in.

As you can see, the ExecutionContext trait is very simple and the most important method is [execute], which responsible for running any Runnable that is passed.

The most simple way to create ExecutionContext is to import, which creates implicit ExecutionContext based on ForkJoinPool, which is suited for many scenarios.


But there are many more ways we can create ExecutionContext. For example,

import java.util.concurrent.Executors
import scala.concurrent.ExecutionContext
implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1000))


import java.util.concurrent.Executors
import java.util.concurrent.ForkJoinPool
implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(new ForkJoinPool())

which will use Runtime.availableProcessors for it’s parallelism level.

For example, we can create very simple ExecutionContext like this

implicit val ec = new ExecutionContext {
    val threadPool = Executors.newFixedThreadPool(1000);

    def execute(runnable: Runnable) {

    def reportFailure(t: Throwable) {}

Or even we can create dummy ExecutionContext that runs computations within the current thread (completely synchronous).

implicit val ec = ExecutionContext.fromExecutor(
  new Executor {
    def execute(runnable: Runnable) { }


A Future represents the result of an asynchronous computation. Future can only be assigned once. After Future object is given result or an exception, it becomes immutable.

The simplest way we can create a Future, is to invoke Future.apply. Let’s create a very simple Future and try to run it:

import scala.concurrent.Future
val f: Future[String] = Future {
    "Nobody calls me chicken!" // Marty McFly

But this will fail, as Future cannot run with ExecutionContext. Let’s import

import scala.concurrent.Future
val f: Future[String] = Future {
    "Nobody calls me chicken!"

Immediately after running that code, your future will start running in the ExecutionContext. You can get result of the future using f variable.




Functional Composition and For-Comprehensions

trait Future[+T]

def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T]