【发布时间】: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 <- f ...与val h = for { x <- Future { 5 } ...不同 - 前者比后者更早地启动Future。 -
我的意思是,如果
scanner是val,那么你的Future在你输入for理解之前就已经开始了。如果是def,则不是。看起来您需要将futureScannedUser设为def或函数() => Future[?] -
事实证明,我同事介绍的修复方法是重新排序 for yield 表达式以及引入 dummy 参数。实际的修复是重新排序,所以我的问题有缺陷。
标签: scala future side-effects dependency-graph