Future

Scala offers an easy way to manage parallel code execution in the form of Futures and callbacks. Future is
a special object that is used to hold a value (usually a result of some computations) that may become available in the
future. Callback if method that will be run when Future is completed. To use Futures in our
application, first we need to import the scala.concurrent package and we need to provide
ExecutionContext. If you’re working with a Play-framework application, the easiest way to obtain execution
context is to use Play’s default thread pool. You can access it by @Inject()(implicit ec:

ExecutionContext).

To execute a block of code concurrently we need to call the Future.apply method – as shown in the example
below.

val futureValue: Future[TypeA] = Future {
   someCode() // Some method that returns value of type TypeA
}

Callbacks

The Future.apply method returns the Future object containing the return value of function
someCode(). To access it we need to add some callback. There are few possibilities here:

  • onComplete
  • foreach
  • map

The first one – onComplete – gives us access to two cases:

  • Success[T] – which holds the return value of successfully completed Future
  • Failed[T] – which hold the value of thrown exception on failed Future
futureValue.onComplete {
   case Success(value) => someCode(value) // Some computation performed on value with type TypeA
   case Failure(exception) => println("Error: " + exception.getMessage)
}

Second and third option allows us to handle only a successful result. Both onComplete and
foreach have a result type of Unit which prevents us from chaining them with other callbacks.
They are mostly used for side effects.

The last option – map – gives us access to map results to any type and returns it in a new Future after
completion of the original future.

val futureValue: Future[TypeA] = Future {
   someCode() // Some method that return value of type TypeA
}

val futureValue2: Future[TypeB] = futureValue.map{ value =>
   someOtherMethod(value) // Some other method of type TypeA => TypeB
}

Parallel execution

Sometimes we need to access values of two or more different Futures. One way of dealing with that problem
is nesting a callback of one future in another callback – as shown in the example below.

val future1: Future[TypeA] = Future{
   someCode()
}

val future2: Future[TypeB] = Future{
   someOtherCode()
}

val future3: Future[Future[TypeC]] = future1.map{ value1 =>
   future2.map{ value2 =>
       someMethodHere(value1, value2) // method of type (TypeA, TypeB) => TypeC
   } // returns Future[TypeC]
}

As we can see, this isn’t the cleanest approach. More importantly, the return type of our nested maps will
be nested Future which will make accessing its result much harder. For that reason Future have
flatMap and withFilter combinators that allow it to be used in for-comprehensions. The same
code as above, but with for-comprehension would look like this:

val future1: Future[TypeA] = Future{
   someCode()
}

val future2: Future[TypeB] = Future{
   someOtherCode()
}

val future: Future[TypeC] = for {
   value1 <- future1
   value2 <- future2
} yield {
   someMethodHere(value1, value2) // method of type (TypeA, TypeB) => TypeC
}

When Future is executed?

While using Future with either of callbacks, for-comprehension or other combinators, it’s very important to
make sure to use it correctly. If we compare two blocks of code:

// Block 1
val future1: Future[TypeA] = Future{
   someCode()
}

val future2: Future[TypeB] = Future{
   someOtherCode()
}

val future = for {
   value1 <- future1
   value2 <- future2
} yield {
   ...
}
// Block 2
val future = for {
   value1 <- Future{someCode()}
   value2 <-  Future{someOtherCode()}
} yield {
   ...
}

On the first sight it may seem like they both do the same thing: run two methods parallel to each other. In reality only
the first one will run concurrently. Since for-comprehensions are extended to (sometimes) nested foreach,
map, flatMap and withFilter the second block would result in running code similar
to this

Future{someCode()}.flatMap{value1 => Future{someOtherCode()}.map{value2 => ... }}

Which means that someCode() will run first, and after its completion someOtherCode() will be
started. Scala starts execution of code in Future.apply when the instance is created, not when it’s
evaluated. By assigning future to value outside of for-comprehensions, we start both of them and then assign callback
methods to them in for-comprehensions.