【问题标题】:Multiple Futures in a Sequence in ScalaScala中序列中的多个期货
【发布时间】:2015-08-13 02:01:30
【问题描述】:

我有两个在 Future 中发生的计算,如下所示:

val comp1 = Future { ... }
val comp2 = Future { ... }

我希望它运行,以便 comp2 始终在 comp1 完成后运行!我知道使用 for 表达式,我可以组成这两个 Future 的like。

for {
  comp1Result <- comp1
  comp2Result <- comp2
} yield { ... }

comp1 在 comp2 之前完成的保证是什么?我的意思是这些是在不同线程中发生的计算,并且不能保证它可能运行的顺序。有没有办法保证订单不阻塞?

【问题讨论】:

  • Scala Async 能解决这个问题吗?
  • 好吧......如果你愿意,你可以把另一个未来放在第一个的 onComplete 中,如果这符合你的目的。
  • 如果我有 4 个在一个链中,而链中的每个 Future 都需要一个才能完成?
  • map 和 flatMap 用于此目的。 future2 = future1.map( val =&gt; 2*val )future2 = future1.flatMap( val =&gt; buildFuture( val ) )

标签: scala future


【解决方案1】:

用于理解的 Scala 是 flatMap 和 map 对“容器类型”(在本例中为期货)调用组合的语法糖,因此您上面的代码等同于:

comp1.flatMap{ comp1Result => comp2 }

这里的大括号内的计算只有在comp1完成并返回成功的结果后才会发生(失败的情况下,内部的计算不会被执行)。

如果您在 for 理解之外定义 comp2 如问题所示,那么您将开始该计算(您只需忽略结果,直到您在 for 理解中),但如果您仅在 for 中定义它理解,那么它不会被启动,除非且直到 comp1 成功完成。因此,请尝试按如下方式重新调整代码:

val comp1 = Future { ... } // or you could put this construction inside the for as well
for {
  comp1Result <- comp1
  comp2Result <- Future { ... } // Only start the computation here
  comp3Result <- Future { ... } // etc. for as many such computations as you need
} yield { ... }

其他替代方法是将 comp2 声明为惰性 val(因此实际上不会对其进行评估 - 开始计算 - 直到它被引用),或者声明为 lambda(例如 val comp2 = () =&gt; Future { ... })并使用以下行调用它

  comp2Result <- comp2()

在 for 理解中。

【讨论】:

  • Scala Async 是否为我提供了与我的情况相同的行为?
  • 在这种特殊情况下可能会出现这种情况,但我强烈建议尽可能避免使用异步(至少在测试之外),并熟悉上述“单子”风格计算,在那里你尽可能长时间地传播“上下文”(这里是 Future monad)。把它想象成构建你的代码,就像你正在为你想要做的事情写一个处方,实际的计算链只在最后可能的时刻执行。
  • 除了熟悉单子计算风格之外,您为什么不建议使用异步?
  • 也许“强烈”推荐是夸大其词 :) 大多数情况下,我觉得它很容易导致混淆什么代码片段在何时(和何地)被执行以及什么结果可用于其他代码位。在简单的情况下,我相信你可以很好地管理它,而且看起来很简单,但是根据我的经验,生产代码很少保持那么简单。不过,我承认我没有使用过 Async,因此使用它的经验更丰富的人可能能够提供更好的指导。对我来说, for 理解感觉更清晰,更容易推理。
猜你喜欢
  • 1970-01-01
  • 2015-06-03
  • 1970-01-01
  • 2021-03-02
  • 2020-07-17
  • 1970-01-01
  • 2020-11-10
  • 2017-03-26
  • 2020-03-31
相关资源
最近更新 更多