1. ホーム
  2. scala

[解決済み] 複数の先物を待つには?

2023-05-05 08:05:46

質問

いくつかの先物があり、そのうちの1つである のどちらかが が失敗するまで待つ必要があるとします。 または はすべて成功します。

例えば 3つの先物があるとします。 f1 , f2 , f3 .

  • もし f1 が成功し f2 が失敗するのを待たずに f3 (そして 失敗 をクライアントに返します)。

  • もし f2 が失敗した場合 f1f3 がまだ実行中であれば、私はそれらを待たずに(そして 失敗 )

  • もし f1 が成功し、その後 f2 が成功するのを待ち続ける。 f3 .

あなたならどう実装しますか?

どのように解決するのですか?

代わりに以下のようなfor-comprehensionを使うことができます。

val fut1 = Future{...}
val fut2 = Future{...}
val fut3 = Future{...}

val aggFut = for{
  f1Result <- fut1
  f2Result <- fut2
  f3Result <- fut3
} yield (f1Result, f2Result, f3Result)

この例では、先物1、2、3が並列にキックオフされます。 そして、for comprehensionで、1、2、3の順に結果が出るまで待ちます。 1か2のどちらかが失敗したら、もう3は待ちません。 もし3がすべて成功したら aggFut の値は3つのスロットを持つタプルを保持し、これは3つの先物の結果に対応します。

さて、もしfut2が先に失敗したら待つのをやめたいという動作が必要な場合、少し難しくなります。 上記の例では、fut1が完了するのを待ってからfut2が失敗したことに気づかなければなりません。 これを解決するには、次のような方法を試してみてください。

  val fut1 = Future{Thread.sleep(3000);1}
  val fut2 = Promise.failed(new RuntimeException("boo")).future
  val fut3 = Future{Thread.sleep(1000);3}

  def processFutures(futures:Map[Int,Future[Int]], values:List[Any], prom:Promise[List[Any]]):Future[List[Any]] = {
    val fut = if (futures.size == 1) futures.head._2
    else Future.firstCompletedOf(futures.values)

    fut onComplete{
      case Success(value) if (futures.size == 1)=> 
        prom.success(value :: values)

      case Success(value) =>
        processFutures(futures - value, value :: values, prom)

      case Failure(ex) => prom.failure(ex)
    }
    prom.future
  }

  val aggFut = processFutures(Map(1 -> fut1, 2 -> fut2, 3 -> fut3), List(), Promise[List[Any]]())
  aggFut onComplete{
    case value => println(value)
  }

さて、これは正しく動作しますが、問題はどの Future を削除するかということです。 Map から取り除くことができます。 結果とその結果を生成した Future を適切に関連付ける方法がある限り、このような方法は機能します。 これは、完了したFutureを再帰的にMapから削除し続け、その後に Future.firstCompletedOf を呼び出し、残りの Futures を実行し、途中で結果を収集します。 きれいではありませんが、あなたが話している動作が本当に必要であれば、これかそれに似たものが動作するでしょう。