web-dev-qa-db-ja.com

Scala futureのonSuccessコールバックが完了するのをどのように待つのですか?

Scalaでは、Awaitを使用して、将来の完了を待つことができます。ただし、そのフューチャーの完了時に実行するコールバックを登録した場合、フューチャーが完了するだけでなく、そのコールバックが完了するまで待機するにはどうすればよいですか?

これは問題を説明するための最小限ですが完全なプログラムです。

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)
    f.onSuccess { case _ =>
      Thread.sleep(10000)
      println("The program waited patiently for this callback to finish.")
    }

    // This waits for `f` to complete but doesn't wait for the callback
    // to finish running.
    Await.ready(f, Duration.Inf)
  }
}

出力は次のようになるはずです。

The program waited patiently for this callback to finish.

代わりに、出力はありません。プログラムは、コールバックが完了する前に終了します。

これは、先に この質問 で回答されている、将来の完了を待つことと同じ問題ではないことに注意してください。

14
Daniel Li

OnSuccessコールバックを使用しないでください。代わりに、Future.map呼び出しで副作用を実行してください。このように、Await onを使用するFuture [Unit]があります。

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)
  }
}

(例のように)成功した​​場合にのみ副作用を実行したい場合は、mapが適切です。失敗した場合にも副作用を実行したい場合は、andThenが適切な方法です。 scala-userのRoland Kuhnによる post を参照してください。

また、しないでください本番コードの近くではThread.sleepを使用しないでください。

20
Rüdiger Klaehn
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")
  }
}

プリント:

F1 is COMPLETED
The program waited patiently for this callback to finish.
F2 is COMPLETED

Promiseの使用はさらに明確です。

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")
  }
}

プリント:

F is COMPLETED
P is COMPLETED
The program waited patiently for this callback to finish.
9
yǝsʞǝla