返回 Future[(Int,Int)] 而不是 (Future[Int],Future[Int])

返回 Future[(Int,Int)] 而不是 (Future[Int],Future[Int])

问题描述:

我有以下期货:

def f1 = Future {1}    
def f2 = Future {2}

我需要以下代码来返回 Future[(Int,Int)] :

I need the following code to return Future[(Int,Int)] :

val future = // function that returns a future
future.flatMap {
    val x1 = f1
    val x2 = f2
    (x1,x2)  // This returns Future[Int],Future[Int] 
}

代替 (Future[Int],Future[Int]) 我需要返回 Future[(Int,Int)] 的函数.如何转换它?

Instead of (Future[Int],Future[Int]) I need the function to return Future[(Int,Int)]. How to convert it?

我将在此处对(当前)已接受的答案提出异议.根据我的评论,这并不是将两个未来压缩在一起的正确方式.正确的方法很简单:

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

另一个答案:

for (x <- f1; y <- f2) yield (x, y)

虽然这会起作用,但在 f2 是产生未来的表达式的情况下并不平行(就像在这个问题中一样).如果是这种情况,在第一个未来完成[1]之前不会构建f2.虽然 zip 已经在 flatMap 方面以同样的方式实现,因为它的参数是严格的,第二个 future 已经在运行(当然取决于执行上下文).

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).

它也更简洁!

[1] - 这可以通过观察 y(由 f1 计算出的值)在 的范围内而看出f2被构造

[1] - this can be seen by observing that y, the value computed by f1, is in scope as f2 is constructed

这很容易证明:

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]

有了这个:

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

但是带拉链

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>)