How do I wait for a Scala future's onSuccess callback to complete?
Solution 1
Don't use an onSuccess callback, but instead do the side effect in a Future.map call. That way, you have a Future[Unit] to use Await on.
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)
}
}
Note that if you want to execute a side effect only in case of success (like in your example), map is appropriate. If you want to execute a side effect also in case of failure, andThen is the right method to use. See this post from Roland Kuhn on scala-user.
Also, please don't use Thread.sleep anywhere near production code.
Solution 2
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")
}
}
prints:
F1 is COMPLETED
The program waited patiently for this callback to finish.
F2 is COMPLETED
Using promises is even more clear:
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")
}
}
prints:
F is COMPLETED
P is COMPLETED
The program waited patiently for this callback to finish.
Dan Li
Updated on July 09, 2022Comments
-
Dan Li almost 2 years
In Scala, I can use
Await
to wait for a future to complete. However, if I have registered a callback to run upon completion of that future, how can I wait not only for the future to complete but also for that callback to finish?Here is a minimal but complete program to illustrate the problem:
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) } }
I expect the output to be:
The program waited patiently for this callback to finish.
Instead, there is no output; the program exits before the callback finishes.
Please note that this is not the same problem as waiting for a future to complete, which has been answered previously at this question.
-
yǝsʞǝla over 8 yearsThere is no point in making 2 futures if you are throwing away the value of the first one. Might as well just run everything in a single future.
-
Rüdiger Klaehn over 8 yearsThis was to stay as close as possible to the given code. In a real application, the first future would produce a value that you actually use.
-
Rüdiger Klaehn over 8 yearsI think promises are a low level API that should not be used if it can be avoided. So the first example using andThen is better.
-
Suma over 8 yearsExactly. Promise is what
andThen
uses under the hood the perform synchronization of theonComplete
handler. -
Dan Li over 8 yearsIf
map
andflatMap
accomplish the same things asonSuccess
(and more, since they can return values), why have theonSuccess
in the API at all? Is it only for symmetry withonFailure
? Or areonSuccess
andonFailure
lower-level constructs upon whichmap
andflatMap
are implemented under the hood? -
Dan Li over 8 yearsExamining the source for Future shows that this does seem to be the case.
-
nedim almost 8 yearsIn the example with
Promise
, if you sleep before printingThis program ...
it will never print. I think the example has the same problem as the original question - there is nothing waiting forp.future.onSuccess
. -
Reaper over 4 yearsIn the first example, if you are doing
Await.ready(f2, Duration.Inf)
andf2
is the continuation off1
, then why do you needAwait.ready(f1, Duration.Inf)
? -
yǝsʞǝla over 4 years@DannyVarod You don't need both Awaits if you just want to wait for everything to complete. However, if you want to see completion of each futures separately, especially that f2 takes much longer than f1, both awaits are useful.