根据这个blog post,理解存在潜在的性能问题。例如:
for {
a <- remoteCallA()
b <- remoteCallB()
} yield {
(a, b)
}
在remoteCallB
完成之前,已remoteCallA
被阻止。博客文章建议我们这样做:
futureA <- remoteCallA()
futureB <- remoteCallB()
for {
a <- futureA
b <- futureB
} yield {
(a, b)
}
这将确保两个远程调用可以同时启动。
我的问题:以上(因此博客作者)是否正确?
我没有看到人们使用这种模式,这让我想知道是否存在通常使用的替代模式。
谢谢
答案 0 :(得分:4)
理解
for {
a <- remoteCallA()
b <- remoteCallB()
} yield {
(a, b)
}
转换为:
remoteCallA().flatmap(a => remoteCallB().map(b => (a,b)))
所以,是的,我相信这位博主是正确的,因为这些调用将是顺序的,而不是并发的。
答案 1 :(得分:2)
同时执行多个期货的常见模式是使用zip
或Future.traverse
。以下是一些例子:
for {
(a, b) <- remoteCallA() zip remoteCallB()
} yield f(a, b)
当有超过2个期货时,这会变得有点麻烦:
for {
((a, b), c) <- remoteCall() zip remoteCallB() zip remoteCallC()
} yield (a, b, c)
在这些情况下,您可以使用Future.sequence
:
for {
Seq(a, b, c) <-
Future.sequence(Seq(remoteCallA(), remoteCallB(), remoteCallC()))
} yield (a, b, c)
或Future.traverse
,如果您有一系列参数,并希望将相同的函数应用于所有参数,这将返回Future
。
但这两种方法都存在问题:如果其中一个Future
提前失败,在其他方法完成之前,您自然可能希望生成的Future
在此时立即失败。但事实并非如此。只有在所有期货完成后,结果Future
才会失败。有关详细信息,请参阅此问题:How to implement Future as Applicative in Scala?