如何等待 Scala 未来的 onSuccess 回调完成?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34808024/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 07:56:36  来源:igfitidea点击:

How do I wait for a Scala future's onSuccess callback to complete?

scalaconcurrencyfuture

提问by Dan Li

In Scala, I can use Awaitto wait for a future to complete. However, if I have registered a callback to run upon completion of that future, how can I wait not only for the future to complete but also for that callback to finish?

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

Here is a minimal but complete program to illustrate the problem:

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

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

I expect the output to be:

我希望输出是:

The program waited patiently for this callback to finish.

Instead, there is no output; the program exits before the callback finishes.

相反,没有输出;程序在回调完成之前退出。

Please note that this is not the same problem as waiting for a future to complete, which has been answered previously at this question.

请注意,这与等待未来完成不同的问题,之前已在此问题中回答过。

回答by Rüdiger Klaehn

Don't use an onSuccess callback, but instead do the side effect in a Future.map call. That way, you have a Future[Unit] to use Await on.

不要使用 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)
  }
}

Note that if you want to execute a side effect only in case of success (like in your example), map is appropriate. If you want to execute a side effect also in case of failure, andThen is the right method to use. See this postfrom Roland Kuhn on scala-user.

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

Also, please don'tuse Thread.sleep anywhere near production code.

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

回答by y?s??la

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")
  }
}

prints:

印刷:

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

Using promises is even more clear:

使用 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")
  }
}

prints:

印刷:

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