Future Composition Patterns
Future composition is the joining of futures into one pipeline. Each step in the pipeline uses the result of the step before it.
flatMap chains a dependent future
The second future needs the first result. flatMap feeds that result into the second future.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
@main def main(): Unit =
val step1 = Future(10)
val combined = step1.flatMap(x => Future(x + 5))
println(Await.result(combined, 5.seconds))map changes the value inside a future
The value inside the future is tripled. No second future is started for this change.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
@main def main(): Unit =
val doubled = Future(6).map(x => x * 3)
println(Await.result(doubled, 5.seconds))A for-comprehension writes the chain plainly
The price is fetched first. The total then adds five to that price.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
@main def main(): Unit =
val total = for
price <- Future(20)
withFee <- Future(price + 5)
yield withFee
println(Await.result(total, 5.seconds))Example
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
@main def main(): Unit =
val result = for
a <- Future(8)
b <- Future(a * 2)
c <- Future(b + 4)
yield c
println(Await.result(result, 5.seconds))Key points
- Pipeline: A pipeline is a chain of steps where each step feeds the next step.
- Use flatMap to run a second future that depends on the first future result.
- Use map to change the value inside a future without starting a new future.
- For-comprehension: A for-comprehension is a shorter way to write a chain of flatMap and map steps.
- Order: Steps in a pipeline run in written order when each step depends on the previous result.
In this example, a starts as 8. Then b doubles a to 16. Then c adds 4 to b to give 20, and the program prints that final value.
Note
A future starts running as soon as it is created, so building a pipeline does not delay the first step.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Scastie
Exercise
What does Future(5).flatMap(x => Future(x * 2)) give when awaited?