How to wait for several Futures?

前端 未结 8 873
故里飘歌
故里飘歌 2020-12-02 06:02

Suppose I have several futures and need to wait until either any of them fails or all of them succeed.

For example: Let there are 3 futures:

8条回答
  •  暖寄归人
    2020-12-02 06:19

    You can do this with futures alone. Here's one implementation. Note that it won't terminate execution early! In that case you need to do something more sophisticated (and probably implement the interruption yourself). But if you just don't want to keep waiting for something that isn't going to work, the key is to keep waiting for the first thing to finish, and stop when either nothing is left or you hit an exception:

    import scala.annotation.tailrec
    import scala.util.{Try, Success, Failure}
    import scala.concurrent._
    import scala.concurrent.duration.Duration
    import ExecutionContext.Implicits.global
    
    @tailrec def awaitSuccess[A](fs: Seq[Future[A]], done: Seq[A] = Seq()): 
    Either[Throwable, Seq[A]] = {
      val first = Future.firstCompletedOf(fs)
      Await.ready(first, Duration.Inf).value match {
        case None => awaitSuccess(fs, done)  // Shouldn't happen!
        case Some(Failure(e)) => Left(e)
        case Some(Success(_)) =>
          val (complete, running) = fs.partition(_.isCompleted)
          val answers = complete.flatMap(_.value)
          answers.find(_.isFailure) match {
            case Some(Failure(e)) => Left(e)
            case _ =>
              if (running.length > 0) awaitSuccess(running, answers.map(_.get) ++: done)
              else Right( answers.map(_.get) ++: done )
          }
      }
    }
    

    Here's an example of it in action when everything works okay:

    scala> awaitSuccess(Seq(Future{ println("Hi!") }, 
      Future{ Thread.sleep(1000); println("Fancy meeting you here!") },
      Future{ Thread.sleep(2000); println("Bye!") }
    ))
    Hi!
    Fancy meeting you here!
    Bye!
    res1: Either[Throwable,Seq[Unit]] = Right(List((), (), ()))
    

    But when something goes wrong:

    scala> awaitSuccess(Seq(Future{ println("Hi!") }, 
      Future{ Thread.sleep(1000); throw new Exception("boo"); () }, 
      Future{ Thread.sleep(2000); println("Bye!") }
    ))
    Hi!
    res2: Either[Throwable,Seq[Unit]] = Left(java.lang.Exception: boo)
    
    scala> Bye!
    

提交回复
热议问题