问题
Consider the following code:
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))
}
}
The output of this code is:
This is sequential
I'm getA
.
.
.
.
.
I'm getB
.
.
.
.
.
A1B1
This is concurrent
I'm getB
.
I'm getA
.
.
.
.
.
.
.
.
.
A2B2
However I would think that in both the cases the Future should execute concurrently. What is it that is making the execution sequential in the first case?
回答1:
It executes sequentially because getB
won't be called but only in the callback function of the Future
returned by getA
. It's explained very well here.
update: so the for
comprehension translates to map
s, flatMap
s and filter
s, which on they turn are just turned into callbacks behind the scenes
回答2:
Can I just use source from Handling futures with for-comp, but if clauses are making things difficult to try to explain how I see the problem(cause I have those source already compiled)?
this for loop:
for {
a <- fooService.getA()
b <- fooService.getB()
} println(a + b)
just gets desugared by scalac using map and flatMap combinators so, lets rewrite it by hand:
fooService.getA.foreach{ a =>
fooService.getB.foreach{ b =>
println(a+b)
}
}
This code seems sequential. If you would have yield keyword in for comprehension,
for {
a <- fooService.getA()
b <- fooService.getB()
} yield(a + b)
then it would be desugared to
fooService.getA.map{ a =>
fooService.getB.flatMap{ b =>
a + b
}
}
Which is also sequential. for comprehension is no more no less than combination of map/flatMap/filter
来源:https://stackoverflow.com/questions/34379025/why-scala-for-comprehension-run-future-functions-sequentially