【问题标题】:Compose stateless futures with side-effecting futures in Scala在 Scala 中用副作用期货组合无状态期货
【发布时间】:2016-05-02 18:03:20
【问题描述】:

在使用 for-yield 结构构成期货时,有些有副作用,有些没有,我引入了一个竞争条件,因为依赖于副作用的未来不会将该副作用的结果作为参数。

简而言之:

future b 读取一个被future 的副作用改变的值 a,但未来 a 不明确依赖于未来 b 的结果,因此可能发生在 b 完成之前阅读。

为了解决这个问题,我的同事引入了一个虚拟函数,将 b 的结果作为参数并将其丢弃。这样做是为了明确依赖关系。

实际代码在这里:

  val futureConnection:Future[(Either[String, (Connection)],Boolean)] =
    for {
      scannerUser <- scanner.orFail("Scanning user not found")
      scannedUser <- futureScannedUser.orFail("Scanned user not found")
      existsInAnyDirection <- connections.existsInAnyDirection(scannerUser, scannedUser)
      connection <- connections.createConnection(scannerUser, scannedUser, form.magicWord, existsInAnyDirection)
    } yield {
      (connection, existsInAnyDirection)
    }

在这种情况下,未来 b

connections.existsInAnyDirection(scannerUser, scannedUser)

和未来的a 带有虚拟参数的

connections.createConnection(scannerUser, scannedUser, form.magicWord, existsInAnyDirection)

请注意,参数existsInAnyDirection 从未在createConnection 中使用。这有效地创建了在 existsInAnyDirection 完成之前无法启动 createConnection 的依赖关系图。

现在问题来了:

有没有更明智的方法来明确依赖关系?


奖金信息

我自己的挖掘告诉我,Scala Futures 根本不能很好地处理副作用。 The methods on the Future trait 处理副作用返回 Unit,而从副作用操作中读取的结果很可能,即错误代码、生成的 ID、任何其他元信息,真的。

【问题讨论】:

  • 我不明白为什么flatMap 不起作用。在创建 Future 时存在重要差异 - 在 flatMap 内部或外部 - 这将影响 Future 开始的时间。也许这就是您错过的细节,这就是您获得比赛条件的原因。 val f = Future { 5 }; val h = for { x &lt;- f ...val h = for { x &lt;- Future { 5 } ... 不同 - 前者比后者更早地启动 Future
  • 我的意思是,如果scannerval,那么你的Future 在你输入for 理解之前就已经开始了。如果是def,则不是。看起来您需要将 futureScannedUser 设为 def 或函数 () =&gt; Future[?]
  • 事实证明,我同事介绍的修复方法是重新排序 for yield 表达式以及引入 dummy 参数。实际的修复是重新排序,所以我的问题有缺陷。

标签: scala future side-effects dependency-graph


【解决方案1】:

Future 正在处理延迟计算的副作用,例如A =&gt; Future[B]

您尝试混合几种不同的副作用,但只合成了其中一种Future[_]

尝试选择第二个容器,这可以是 Product 或 State,这取决于您的副作用并考虑副作用的构成方式(可能您需要 mod 和转换器)。在您的代码看起来像(最简单的情况)之后:

for {
  scannerUser <- scanner.orFail("Scanning ...")
  (scannedUser, magicWord) <- futureScannedUser.orFail("Scanned ...")      
  connection <- connections.createConnection(scannerUser, scannedUser, magicWord)
} yield {
  (connection, existsInAnyDirection)
}

// OR

for {
  (scannerUser, state) <- scanner.orFail("Scanning ...")
  (scannedUser, nextState) <- futureScannedUser(state).orFail("Scanned ...")      
  connection <- connections.createConnection(scannerUser, scannedUser, nextState)
} yield {
  (connection, existsInAnyDirection)
}

【讨论】:

    猜你喜欢
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 2020-01-20
    • 2018-06-15
    • 2015-08-08
    • 2020-05-09
    相关资源
    最近更新 更多