web-dev-qa-db-ja.com

Scala先物のシーケンスを待っています

次のようなコードが両方の先物を待つことを望んでいましたが、そうではありません。

object Fiddle {
  val f1 = Future {
    throw new Throwable("baaa") // emulating a future that bumped into an exception
  }

  val f2 = Future {
    Thread.sleep(3000L) // emulating a future that takes a bit longer to complete
    2
  }

  val lf = List(f1, f2) // in the general case, this would be a dynamically sized list

  val seq = Future.sequence(lf) 

  seq.onComplete {
    _ => lf.foreach(f => println(f.isCompleted))
  }
}

val a = FuturesSequence

seq.onCompleteは、それらがすべて完了するのを待ってから完了しますが、完了しません。結果は次のとおりです。

true
false

.sequenceはscala.concurrent.Futureのソースでは少し理解しづらかったのですが、(動的なサイズの)シーケンスのすべての元々の未来を待つ並列をどのように実装するのか、またはここで何が問題になるのでしょうか。

編集:関連する質問: https://worldbuilding.stackexchange.com/questions/12348/how-do-you-prove- youre-from-the-future :)

26
matanster

Future.sequenceによって生成されるFutureは、次のいずれかの場合に完了します。

  • すべての先物が正常に完了した、または
  • 先物の1つが失敗しました

2番目のポイントはあなたのケースで何が起こっているかであり、ラッピングFutureは1つのFutureしか保持できないため、ラップされたThrowableの1つが失敗するとすぐに完了するのが理にかなっています失敗のケース。結果は同じ失敗になるので、他の先物を待つ意味はありません。

21
Ionuț G. Stan

これは、前の回答をサポートする例です。標準のScala APIを使用してこれを行う簡単な方法があります。

この例では、3つの先物を作成しています。これらは、それぞれ5、7、および9秒で完了します。 _Await.result_の呼び出しは、すべての先物が解決されるまでブロックされます。 3つの先物すべてが完了すると、aList(5,7,9)に設定され、実行が続行されます。

さらに、将来のいずれかで例外がスローされた場合、_Await.result_はすぐにブロックを解除して例外をスローします。 Exception(...)行のコメントを外して、実際に動作していることを確認してください。

_  try {
    val a = Await.result(Future.sequence(Seq(
      Future({
        blocking {
          Thread.sleep(5000)
        }
        System.err.println("A")
        5
      }),
      Future({
        blocking {
          Thread.sleep(7000)
        }
        System.err.println("B")
        7
        //throw new Exception("Ha!")
      }),
      Future({
        blocking {
          Thread.sleep(9000)
        }
        System.err.println("C")
        9
      }))),
      Duration("100 sec"))

    System.err.println(a)
  } catch {
    case e: Exception ⇒
      e.printStackTrace()
  }
_
3
Jason Smith

Seq[Future[T]]暗黙的なクラスを介した独自のonCompleteメソッドを使用:

  def lift[T](f: Future[T])(implicit ec: ExecutionContext): Future[Try[T]] =
    f map { Success(_) } recover { case e => Failure(e) }

  def lift[T](fs: Seq[Future[T]])(implicit ec: ExecutionContext): Seq[Future[Try[T]]] =
    fs map { lift(_) }

  implicit class RichSeqFuture[+T](val fs: Seq[Future[T]]) extends AnyVal {
    def onComplete[U](f: Seq[Try[T]] => U)(implicit ec: ExecutionContext) = {
      Future.sequence(lift(fs)) onComplete {
        case Success(s) => f(s)
        case Failure(e) => throw e // will never happen, because of the Try lifting
      }
    }
  }

次に、特定のMWEで次のことができます。

  val f1 = Future {
    throw new Throwable("baaa") // emulating a future that bumped into an exception
  }

  val f2 = Future {
    Thread.sleep(3000L) // emulating a future that takes a bit longer to complete
    2
  }

  val lf = List(f1, f2)

  lf onComplete { _ map {
    case Success(v) => ???
    case Failure(e) => ???
  }}

このソリューションには、単一のフューチャーで行うように、一連のフューチャーでonCompleteを呼び出すことができるという利点があります。

1
Bruno