How do I wait for a Scala future's onSuccess callback to complete?

22,835

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.
Share:
22,835
Dan Li
Author by

Dan Li

Updated on July 09, 2022

Comments

  • Dan Li
    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
    yǝsʞǝla over 8 years
    There 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
    Rüdiger Klaehn over 8 years
    This 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
    Rüdiger Klaehn over 8 years
    I 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
    Suma over 8 years
    Exactly. Promise is what andThen uses under the hood the perform synchronization of the onComplete handler.
  • Dan Li
    Dan Li over 8 years
    If map and flatMap accomplish the same things as onSuccess (and more, since they can return values), why have the onSuccess in the API at all? Is it only for symmetry with onFailure? Or are onSuccess and onFailure lower-level constructs upon which map and flatMap are implemented under the hood?
  • Dan Li
    Dan Li over 8 years
    Examining the source for Future shows that this does seem to be the case.
  • nedim
    nedim almost 8 years
    In the example with Promise, if you sleep before printing This program ... it will never print. I think the example has the same problem as the original question - there is nothing waiting for p.future.onSuccess.
  • Reaper
    Reaper over 4 years
    In the first example, if you are doing Await.ready(f2, Duration.Inf) and f2 is the continuation of f1, then why do you need Await.ready(f1, Duration.Inf)?
  • yǝsʞǝla
    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.