Waiting for another future to end to return a func

2019-07-13 02:35发布

Let's say I have a function func1 that needs to return a Future with two integers. Each of the two values are returned by independent futures, like so:

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

def func1 : Future[(Int,Int)] = {

    val future1 = f1
    future1.map { result1 =>
       result1 * 10
    }

    val future2 = f2
    future2.map { result2 =>
       result2 * 20
    }

} 

I need future1 wait until future2 ends (or vice versa) to return both results as (Int,Int). How can this be accomplished?

标签: scala future
3条回答
成全新的幸福
2楼-- · 2019-07-13 02:52

You can use a for-comprehension for futures that have already started like this:

val f1: Future[Int] = ???
val f2: Future[Int] = ???
val f3: Future[Int] = ???

val futureInts: Future[(Int, Int, Int)] = for {
  result1 <- f1
  result2 <- f2
  result3 <- f3
} yield (result1, result2, result3)

If the futures were assigned to lazy vals or defs then this wouldn't work, because the futures would not have been started (if you start the futures inside the for comprehension, then they will be executed sequentially). Here is an example of starting them, and then waiting for them with for.

Example:

val f1: Future[Int] = Future {
  println("starting f1")
  Thread.sleep(1000)
  1
}
val f2: Future[Int] = Future {
  println("starting f2")
  Thread.sleep(3000)
  2
}
val f3: Future[Int] = Future {
  println("starting f3")
  Thread.sleep(2000)
  3
}

val futureInts: Future[(Int, Int, Int)] = for {
  result1 <- f1
  result2 <- f2
  result3 <- f3
} yield (result1, result2, result3)

futureInts.map {
  case tuple => println(tuple)
}

Output:

starting f1 // These first 
starting f3 // threes statements
starting f2 // happen right away.

(1,2,2)     // Then this prints a three seconds later

In your case you could do this:

def func1 : Future[(Int,Int)] = {

  // Start futures
  val future1 = f1.map(_ * 10)
  val future2 = f2.map(_ * 20)

  // Wait for both futures, and return a tuple
  for {
    result1 <- future1
    result2 <- future2
  } yield (result1, result2)

}
查看更多
家丑人穷心不美
3楼-- · 2019-07-13 03:04

A for-comprehension is the best option here:

scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> import concurrent.ExecutionContext.Implicits.global
import concurrent.ExecutionContext.Implicits.global

scala> def f1 = Future{1}
f1: scala.concurrent.Future[Int]

scala> def f2 = Future{2}
f2: scala.concurrent.Future[Int]

scala> for {result1 <- f1; result2 <- f2} yield (result1 * 10, result2 * 20)
res0: scala.concurrent.Future[(Int, Int)] = scala.concurrent.impl.Promise$DefaultPromise@71f67a79

More information can be found here and here.

Note: this will run the two Futures in sequence while Cyrille Corpet's solution will run them in parallel.

查看更多
再贱就再见
4楼-- · 2019-07-13 03:07

That's precisely what the zip method on futures does:

val futurePair: Future[(Int, Int)] = future1.zip(future2)

Note that if you haven't instantiated your futures before (say, if future1 and future2 are defs, not vals), this will run the two computations in parallel, while a for comprehension (or flatMap) would wait for the first one to succeed before starting the second one.

查看更多
登录 后发表回答