次のようなコードが両方の先物を待つことを望んでいましたが、そうではありません。
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 :)
Future.sequence
によって生成されるFuture
は、次のいずれかの場合に完了します。
2番目のポイントはあなたのケースで何が起こっているかであり、ラッピングFuture
は1つのFuture
しか保持できないため、ラップされたThrowable
の1つが失敗するとすぐに完了するのが理にかなっています失敗のケース。結果は同じ失敗になるので、他の先物を待つ意味はありません。
これは、前の回答をサポートする例です。標準のScala APIを使用してこれを行う簡単な方法があります。
この例では、3つの先物を作成しています。これらは、それぞれ5、7、および9秒で完了します。 _Await.result
_の呼び出しは、すべての先物が解決されるまでブロックされます。 3つの先物すべてが完了すると、a
がList(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()
}
_
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
を呼び出すことができるという利点があります。