I'm going to take issue with the (currently) accepted answer here. As per my comment, this is not really the correct way to zip together two futures. the correct way is simply this:
f1 zip f2
The other answer:
for (x <- f1; y <- f2) yield (x, y)
Whilst this will work, it is not parallel in the case that f2
is an expression yielding a future (as it is in this question). If this is the case, f2
will not be constructed until the first future has completed [1]. Whilst zip
has been implemented in terms of flatMap
in the same way, because its argument is strict, the second future is already running (subject to the execution context of course).
It's also more succinct!
[1] - this can be seen by observing that y
, the value computed by f1
, is in scope as f2
is constructed
Appendix
This can be demonstrated easily:
scala> import scala.concurrent._; import ExecutionContext.Implicits.global
import scala.concurrent._
import ExecutionContext.Implicits.global
scala> def createAndStartFuture(i: Int): Future[Int] = Future {
| println(s"starting $i in ${Thread.currentThread.getName} at ${java.time.Instant.now()}")
| Thread.sleep(20000L)
| i
| }
createAndStartFuture: (i: Int)scala.concurrent.Future[Int]
With this:
scala> for (x <- createAndStartFuture(1); y <- createAndStartFuture(2)) yield (x, y)
starting 1 in scala-execution-context-global-34 at 2017-05-05T10:29:47.635Z
res15: scala.concurrent.Future[(Int, Int)] = Future(<not completed>)
// Waits 20s
starting 2 in scala-execution-context-global-32 at 2017-05-05T10:30:07.636Z
But with zip
scala> createAndStartFuture(1) zip createAndStartFuture(2)
starting 1 in scala-execution-context-global-34 at 2017-05-05T10:30:45.434Z
starting 2 in scala-execution-context-global-32 at 2017-05-05T10:30:45.434Z
res16: scala.concurrent.Future[(Int, Int)] = Future(<not completed>)