为什么 Scala for comprehension 运行 Future 按顺序运行?
Why Scala for comprehension run Future functions sequentially?
考虑以下代码:
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 应该同时执行。在第一种情况下是什么使执行顺序?
我可以只使用来自 的源代码吗
试图解释我是如何看待问题的(因为我已经编译了那些源代码)?
这个 for 循环:
for {
a <- fooService.getA()
b <- fooService.getB()
} println(a + b)
scalac 使用 map 和 flatMap 组合器对它进行了脱糖,所以,让我们手动重写它:
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
的组合
它是顺序执行的,因为getB
不会被调用,而只会在getA
返回的Future
的回调函数中被调用。解释的很好here.
update:因此 for
理解转化为 map
s、flatMap
s 和 filter
s,在他们只是 turned into callbacks 在幕后
考虑以下代码:
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 应该同时执行。在第一种情况下是什么使执行顺序?
我可以只使用来自
这个 for 循环:
for {
a <- fooService.getA()
b <- fooService.getB()
} println(a + b)
scalac 使用 map 和 flatMap 组合器对它进行了脱糖,所以,让我们手动重写它:
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
的组合它是顺序执行的,因为getB
不会被调用,而只会在getA
返回的Future
的回调函数中被调用。解释的很好here.
update:因此 for
理解转化为 map
s、flatMap
s 和 filter
s,在他们只是 turned into callbacks 在幕后