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

前端 未结 2 386
醉梦人生
醉梦人生 2020-12-31 07:10

In Scala, I can use Await to 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

相关标签:
2条回答
  • 2020-12-31 07:45
    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:

    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.
    
    0 讨论(0)
  • 2020-12-31 07:51

    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.

    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 post from Roland Kuhn on scala-user.

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

    0 讨论(0)
提交回复
热议问题