This is the second of several posts describing protips for scala.concurrent.Future
.
For the previous post, click here.
Futures in for-comprehensions
A common choice when working with scala.concurrent.Future
’s is to use them inside for-comprehensions. What I am about to show you is a common mistake to make, and how to avoid it.
The Problem
So let’s say you’ve written something similar to the following:
def doSomething(someParameter: SomeType)
(implicit ec: ExecutionContext): Future[Something] =
for {
v1 <- Future(someCalculation())
v2 <- Future(someOtherCalculation())
v3 <- Future(someDifferentCalculation())
} yield doSomethingWith(v1, v2, v3)
Well, it turns out that since the three calculations are independent, that the desire—and intent—of the method is to execute all the calculations in parallel and then combine their results in the doSomethingWith
-method, and return everything inside a Future
.
However, since for-comprehensions are simply syntactic bacon for flatMap
, map
, filter
/withFilter
, and foreach
—v2
will only be created once v1
has completed, and v3
will once be created once both v1
and v2
are completed.
What to do?
The Solution
What if we instead leverage the fact that for-comprehensions support value definitions
once the “generator”—the first x <- y
expression—has been given, and that consecutive value definitions
are evaluated as regular value definitions—immediately after eachother?
def doSomething(someParameter: SomeType)
(implicit ec: ExecutionContext): Future[Something] =
for {
_ <- Future.unit // Or Future.successful(())
f1 = Future(someCalculation())
f2 = Future(someOtherCalculation())
f3 = Future(someDifferentCalculation())
v1 <- f1
v2 <- f2
v3 <- f3
} yield doSomethingWith(v1, v2, v3)
The solution above will execute f1
, f2
, and f3
—if possible—in parallel, and then extract their values and combine them with the doSomethingWith
method. Just like we intended when we wrote the for-comprehension the first time.
Click here for the next part in this blog series.
Cheers,
√