【问题标题】:How do I wait for a Scala future's onSuccess callback to complete?如何等待 Scala 未来的 onSuccess 回调完成?
【发布时间】:2016-04-20 20:48:50
【问题描述】:

在 Scala 中,我可以使用 Await 等待未来完成。但是,如果我注册了一个回调以在该未来完成时运行,我如何不仅等待未来完成,还等待该回调完成?

这里有一个最小但完整的程序来说明这个问题:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{ Await, Future }

object Main {
  def main(args: Array[String]): Unit = {
    val f: Future[Int] = Future(0)
    f.onSuccess { case _ =>
      Thread.sleep(10000)
      println("The program waited patiently for this callback to finish.")
    }

    // This waits for `f` to complete but doesn't wait for the callback
    // to finish running.
    Await.ready(f, Duration.Inf)
  }
}

我希望输出是:

The program waited patiently for this callback to finish.

相反,没有输出;程序在回调结束前退出。

请注意,这与等待未来完成不是同一个问题,之前已在 this question 回答过。

【问题讨论】:

    标签: scala concurrency future


    【解决方案1】:

    不要使用 onSuccess 回调,而是在 Future.map 调用中产生副作用。这样,你就有了一个 Future[Unit] 来使用 Await。

    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration.Duration
    import scala.concurrent.{ Await, Future }
    
    object Main {
      def main(args: Array[String]): Unit = {
        val f: Future[Int] = Future(0)
        val f2: Future[Unit] = f.map { x =>
          Thread.sleep(10000)
          println("The program waited patiently for this callback to finish.")
        }
    
        Await.ready(f2, Duration.Inf)
      }
    }
    

    请注意,如果您只想在成功的情况下执行副作用(例如在您的示例中),则 map 是合适的。如果您想在失败的情况下也执行副作用,那么使用正确的方法。请参阅 Roland Kuhn 在 scala-user 上的 post

    另外,请不要在生产代码附近的任何地方使用 Thread.sleep。

    【讨论】:

    • 如果你丢掉第一个期货的价值,那么做两个期货是没有意义的。还不如在一个未来运行所有东西。
    • 这是为了尽可能接近给定的代码。在实际应用中,第一个未来会产生一个您实际使用的值。
    • 如果mapflatMap 完成与onSuccess 相同的事情(以及更多,因为它们可以返回值),为什么API 中有onSuccess 呢?只是为了和onFailure对称吗?或者onSuccessonFailure 是在底层实现mapflatMap 的底层结构?
    • 检查Future 的来源表明情况确实如此。
    【解决方案2】:
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration.Duration
    import scala.concurrent.{ Await, Future }
    import scala.util._
    
    object Main {
      def main(args: Array[String]): Unit = {
        val f1: Future[Int] = Future(0)
        val f2 = f1 andThen {
          case Success(v) =>
            Thread.sleep(10000)
            println("The program waited patiently for this callback to finish.")
          case Failure(e) =>
            println(e)
        }
    
        Await.ready(f1, Duration.Inf)
        println("F1 is COMPLETED")
        Await.ready(f2, Duration.Inf)
        println("F2 is COMPLETED")
      }
    }
    

    打印:

    F1 is COMPLETED
    The program waited patiently for this callback to finish.
    F2 is COMPLETED
    

    使用 Promise 更加清晰:

    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration.Duration
    import scala.concurrent._
    import scala.util._
    
    object Main {
      def main(args: Array[String]): Unit = {
        val f: Future[Int] = Future(0)
        val p = Promise[Unit]()
        p.future.onSuccess { case _ =>
          println("The program waited patiently for this callback to finish.")
        }
        f.onSuccess { case _ =>
          Thread.sleep(10000)
          p.success(())
        }
    
        Await.ready(f, Duration.Inf)
        println("F is COMPLETED")
        Await.ready(p.future, Duration.Inf)
        println("P is COMPLETED")
      }
    }
    

    打印:

    F is COMPLETED
    P is COMPLETED
    The program waited patiently for this callback to finish.
    

    【讨论】:

    • 我认为 Promise 是一种低级 API,如果可以避免,就不应该使用它。所以第一个使用 andThen 的例子更好。
    • 没错。 Promise 是 andThen 在后台使用 onComplete 处理程序的执行同步。
    • Promise 的示例中,如果您在打印This program ... 之前睡眠,它将永远不会打印。我认为这个例子和原来的问题有同样的问题——没有什么在等待p.future.onSuccess
    • 在第一个例子中,如果你在做Await.ready(f2, Duration.Inf),而f2f1的延续,那你为什么需要Await.ready(f1, Duration.Inf)呢?
    • @DannyVarod 如果您只想等待一切完成,则不需要两个等待。但是,如果您想分别查看每个期货的完成情况,尤其是 f2 比 f1 花费的时间要长得多,那么两个等待都是有用的。
    猜你喜欢
    • 2019-04-20
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 2016-03-09
    • 2016-02-17
    • 2014-11-11
    • 1970-01-01
    • 2019-10-08
    相关资源
    最近更新 更多