为什么Scala for comprehension顺序运行Future函数?

时间:2015-12-20 08:00:31

标签: scala concurrency future

请考虑以下代码:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._

object FutureFor {
  def getA(n: Int) = {
    val x: Future[String] = Future {
      println("I'm getA")
      for (i <- 1 to 5) {
        println(".")
        Thread.sleep(200)
      }
      s"A$n"
    }
    x
  }

  def getB(n: Int) = {
    val x: Future[String] = Future {
      println("I'm getB")
      for (i <- 1 to 5) {
        println(".")
        Thread.sleep(200)
      }
      s"B$n"
    }
    x
  }

  def main(args: Array[String]) = {

    println("\nThis is sequential")
    val rs1 = for {
      a <- getA(1)
      b <- getB(1)
    } yield (a + b)
    println(Await.result(rs1, 1 minute))

    println("\nThis is concurrent")
    val first = getA(2)
    val second = getB(2)
    val rs2 = for {
      a <- first
      b <- second
    } yield (a + b)

    println(Await.result(rs2, 1 minute))
  }

}

此代码的输出为:

This is sequential
I'm getA
.
.
.
.
.
I'm getB
.
.
.
.
.
A1B1

This is concurrent
I'm getB
.
I'm getA
.
.
.
.
.
.
.
.
.
A2B2

但是我认为在两种情况下,Future应该同时执行。在第一种情况下,使执行顺序是什么?

2 个答案:

答案 0 :(得分:4)

它会按顺序执行,因为getB不会被调用,而只会在Future返回的getA的回调函数中调用。它解释得非常好here

更新:因此for理解转换为map s,flatMapfilter s,它们只是{ {3}}在幕后

答案 1 :(得分:2)

我可以使用来自Handling futures with for-comp, but if clauses are making things difficult的来源吗? 试图解释我是如何看待问题的(因为我已经编译了这些来源)?

这个for循环:

for {
  a <- fooService.getA()
  b <- fooService.getB()
} println(a + b)

使用map和flatMap组合器让scalac感到害怕,让我们手工重写:

fooService.getA.foreach{ a =>
  fooService.getB.foreach{ b =>
    println(a+b)
  }
}

此代码似乎是顺序的。如果你有理解中的yield关键字,

for {
  a <- fooService.getA()
  b <- fooService.getB()
} yield(a + b)

然后它会被玷污

fooService.getA.map{ a =>
  fooService.getB.flatMap{ b =>
    a + b
  }
}

这也是顺序的。理解力不亚于map / flatMap / filter

的组合