我们有一个Scala Play网络应用程序,它将许多数据库操作作为HTTP请求的一部分,每个都是未来。通常我们会将期货泡沫化为异步控制器操作,并让Play处理等待它们。是否需要等待所有期货以保证其执行?
但我也注意到在一些地方我们不会冒泡未来或者等待它完成。我认为这是不好的,因为这意味着如果将来失败,HTTP请求不会失败,但它确实能保证未来将被执行,因为没有任何事情会等到它的结果?播放HTTP请求后播放是否会放弃未预期的期货,或者让它们在后台运行?
我们有一个Scala Play网络应用程序,它将许多数据库操作作为HTTP请求的一部分,每个都是未来。通常我们会将期货泡沫化为异步控制器操作,并让Play处理等待它们。是否需要等待所有期货以保证其执行?
但我也注意到在一些地方我们不会冒泡未来或者等待它完成。我认为这是不好的,因为这意味着如果将来失败,HTTP请求不会失败,但它确实能保证未来将被执行,因为没有任何事情会等到它的结果?播放HTTP请求后播放是否会放弃未预期的期货,或者让它们在后台运行?
TL;发送HTTP响应后DR
Future
秒。Future
中的任何一个出现故障,将不会报告错误。龙版
当HTTP响应已发送您的期货就不会被撞死。你可以尝试为自己是这样的:
def futuresTest = Action.async { request =>
println(s"Entered futuresTest at ${LocalDateTime.now}")
val ignoredFuture = Future{
var i = 0
while (i < 10) {
Thread.sleep(1000)
println(LocalDateTime.now)
i += 1
}
}
println(s"Leaving futuresTest at ${LocalDateTime.now}")
Future.successful(Ok)
}
然而,你是正确的,如果有期货的请求失败也不会失败。如果这是一个问题,那么你可以使用来构建期货以理解或flatMaps。下面是你可以做什么的例子(我假设你的期货只进行侧efects(Future[Unit]
)
为了让你的期货使用并联执行
val dbFut1 = dbCall1(...)
val dbFut2 = dbCall2(...)
val wsFut1 = wsCall1(...)
val fut = for(
_ <- dbFut1;
_ <- dbFut2;
_ <- wsFut1
) yield()
fut.map(_ => Ok)
让他们按顺序执行
val fut = for(
_ <- dbCall1(...);
_ <- dbCall2(...);
_ <- wsCall2(...)
) yield()
fut.map(_ => Ok)
它实际上连保证未来将在所有被执行,因为 没有什么要等待它的结果?会播放下降 HTTP请求送达后未期待的期货,还是让 在后台运行?
这个问题实际上比Play更深入。你通常会问:“如果我不同步等待未来,我怎么能保证它会在没有GCed的情况下实际完成?”。要回答这个问题,我们需要了解GC如何实际查看线程。从GC角度来看,线程就是我们所说的“根”。这样的根就是堆遍历它的对象并查看哪些可以收集的起点。例如,在根中也有静态字段,这些字段在应用程序的整个生命周期中都是已知的。
所以,当你看到它这样,并认为一个什么样的Future
实际上做,这是队列从可用线程池通过底层ExecutorService
一个专门的线程(我们称之为ExecutionContext
运行的函数在Scala中),你会发现即使你没有等待完成,JVM运行时确实保证你的Future
将运行完成。至于包装该函数的对象Future
,它保存对该未完成函数体的引用,因此Future
本身未被收集。
当你从这个角度看想想看,这是完全合乎逻辑的,因为Future
的执行异步发生,而我们平时使用的延续,如map
,flatMap
,onComplete
等