I have some code like
//all data have different types
val data1Future = loadData1(params)
val data2Future = loadData2(params)
val data3Future = loadData3(params)
def saveResult(rez): Future[_] = ???
data1Future.flatMap { data1 =>
data2Future.flatMap { data2 =>
data3Future.flatMap { data3 =>
//do some computation
//several rows and several vals
val rez = ???
saveResult(rez)
}
}
}
But it is a litle bit ugly :) Unfortunatelly, I can't use for comprehension since I need something like "flatYield"
for {
data1 <- data1Future
data1 <- data1Future
data1 <- data1Future
} flatYield {
//do some computation
//several rows and several vals
val rez = data1 + data2 + data3
saveResult(rez)
}
Do you know pattern that is such elegant as "for comprehension" but with flatMap instead of map at the end of the chain?
You can do this:
for {
data1 <- data1Future
data2 <- data2Future
data3 <- data3Future
rez = {
//do some computation
//several rows and several vals
data1 + data2 + data3
}
r <- saveResult(rez)
} yield r
This translates to
data1Future.flatMap { data1 =>
data2Future.flatMap { data2 =>
data3Future.flatMap { data3 =>
val rez = {
//do some computation
//several rows and several vals
data1 + data2 + data3
}
saveResult(rez).map(r => r)
}
}
}
which is isomorphic to your code.
It seems like you just want another line within your for-comprehension, and all of that "other computation" should go within another function to keep things clean:
for {
data1 <- data1Future
data2 <- data2Future
data3 <- data3Future
rez <- otherFunction(data1, data2, data3)
} yield rez
def otherFunction(d1: ?, d2: ?, d3: ?): Future[?] = {
//do some computation
//several rows and several vals
}
Alternatively you can use something like this:
(for {
data1 <- data1Future
data2 <- data2Future
data3 <- data3Future
} yield {
(data1, data2, data3)
}) flatMap { case (data1, data2, data3) =>
//do some computation
//several rows and several vals
saveResult(rez)
}
Using a for comprehension (like your stated goal) is equivalent to the defining the futures in a sequence of flatMap operations will wait for the result of each future before going to the next. Since your futures don't depend on each other you can start them all without waiting for the previous one to complete - this is what you have done in your initial example.
Start them concurrently and store the futures in a sequence use Future.sequence to turn the collection of futures into a single Future that won't complete until ALL of them complete (or any one of them fails). Then save your result when the future Completes.
val data1Future = loadData1(params) //type = Future[foo]
val data2Future = loadData2(params)
val data3Future = loadData3(params)
val listOfFutures = List(data1Future,data2Future,data3Future) //type = List[Future[foo]]
val futureOfList = Future.sequence(listOfFutures) //type = Future[List[foo]]
futureOfList.onComplete( list => saveResult(list.reduce(_ + _))