是否需要等待所有期货以保证其执行?

时间:2017-01-26 08:40:50

标签: scala playframework

我们有一个Scala Play webapp,它在HTTP请求中执行许多数据库操作,每个操作都是Future。通常我们将Futures冒泡到异步控制器动作并让Play处理等待它们。

但我也注意到,在很多地方,我们不会冒泡未来甚至等待它完成。我认为这很糟糕,因为这意味着如果未来失败,HTTP请求不会失败,但它实际上甚至会保证将来会执行,因为没有什么会等待它的结果?在提供HTTP请求后,Play会丢弃期待已久的未来,还是让它们在后台运行?

2 个答案:

答案 0 :(得分:2)

TL; DR

  1. 发送HTTP响应后播​​放不会终止Future
  2. 如果Future的任何一个失败,将不会报告错误。
  3. 长版

    发送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)
      }
    

    但是,如果任何期货失败,请求不会失败。如果这是一个问题,那么您可以使用 for comprehension flatMaps 撰写期货。以下是您可以做的一个示例(我假设您的期货只执行副作用(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)
    

答案 1 :(得分:1)

  

它实际上甚至保证将来会被执行,   既然没有什么可以等待它的结果呢?将播放下降   在HTTP请求被提供或离开之后,期待已久的未来   他们在后台跑?

这个问题实际上比Play更深入。你通常会问:“如果我不同步等待未来,我怎么能保证它不会在没有GC的情况下完成?”。要回答这个问题,我们需要了解GC实际上如何查看线程。从GC的角度来看,一个线程就是我们所说的“根”。这样的根是堆遍历其对象的起点,并查看哪些对象有资格进行收集。根中还有静态字段,例如,已知它们在应用程序的整个生命周期中都存在。

所以,当你这样看的时候,想一想Future实际上做了什么,这是一个在一个专用线程上运行的函数,该线程来自可通过底层ExecutorService获得的线程池(我们在Scala中将其称为ExecutionContext),您会看到即使您没有等待完成,JVM运行时仍可确保您的Future将运行完成。对于包装函数的Future对象,它保存对未完成的函数体的引用,因此不会收集Future本身。

当你从这个角度思考它时,它是完全合乎逻辑的,因为Future的执行是异步发生的,我们通常使用map之类的延续以异步方式继续处理它, flatMaponComplete