Suppose I want to make coffee. This involves 4 steps:
- 1a. grind coffee beans
- 1b. heat water
- 2. combine
- 3. filter
All these steps take time, so they return a Future. This is our domain:
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global //We need an executionContext to run futures
import scala.concurrent.duration._ //This provides the "1 second" syntax
class CoffeeBeans()
class GroundCoffee()
class ColdWater()
class WarmWater()
class UnfilteredCoffee()
class FilteredCoffee()
//we start out with beans and cold water
val beans = new CoffeeBeans()
val water = new ColdWater()
def grindBeans(beans: CoffeeBeans) = Future { new GroundCoffee() }
def heatWater(water: ColdWater) = Future { new WarmWater() }
def combine(groundCoffee: GroundCoffee, water: WarmWater) = Future { new UnfilteredCoffee() }
def filter(unfilteredCoffee: UnfilteredCoffee) = Future { new FilteredCoffee() }One way we could chain the futures together is with onSuccess:
val fGrind: Future[GroundCoffee] = grindBeans(beans)
val fHeat: Future[WarmWater] = heatWater(water)
val fStep1: Future[(GroundCoffee, WarmWater)] = fGrind.zip(fHeat)
fStep1.onSuccess { case (groundCoffee, warmWater) =>
val fCombine: Future[UnfilteredCoffee] = combine(groundCoffee, warmWater)
fCombine.onSuccess { case unfilteredCoffee =>
val fFilter: Future[FilteredCoffee] = filter(unfilteredCoffee)
fFilter.onSuccess { case successCoffee =>
println(s"$successCoffee is ready!")
}
}
}
Thread.sleep(1000)//wait for the coffee to be readyUsing flatmap we can simplify this to:
val fGrind: Future[GroundCoffee] = grindBeans(beans)
val fHeat: Future[WarmWater] = heatWater(water)
val fStep1: Future[(GroundCoffee, WarmWater)] = fGrind.zip(fHeat)
val fCombine: Future[UnfilteredCoffee] = fStep1.flatMap {
case (groundCoffee, warmWater) => combine(groundCoffee, warmWater)
}
val fFilter: Future[FilteredCoffee] = fCombine.flatMap {
case unfilteredCoffee => filter(unfilteredCoffee)
}
val flatmapCoffee: FilteredCoffee = Await.result(fFilter, 1 second)Notice that we have the future of our filtered coffee at the highest level. This is nice! We can return it, or call Await.Result on it to wait for our coffee to be ready.
Since a for comprehension is a shorter way of writing flatMaps, we can simplify it to this:
val fFor = for {
groundCoffee <- grindBeans(beans)
warmWater <- heatWater(water)
unfilteredCoffee <- combine(groundCoffee, warmWater)
} yield filter(unfilteredCoffee)
val forCoffee: FilteredCoffee = Await.result(fFilter, 1 second)cheers!
| Reference: | Chaining futures in scala from our JCG partner Tammo Sminia at the JDriven blog. |
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Thank you!
We will contact you soon.

This site uses Akismet to reduce spam. Learn how your comment data is processed.