Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 803
Dan Li Avatar asked Jan 15 '16 09:01

Dan Li


People also ask

How do you complete a Future in Scala?

The most general form of registering a callback is by using the onComplete method, which takes a callback function of type Try[T] => U . The callback is applied to the value of type Success[T] if the future completes successfully, or to a value of type Failure[T] otherwise.

What is await result in Scala?

Await. result tries to return the Future result as soon as possible and throws an exception if the Future fails with an exception while Await. ready returns the completed Future from which the result (Success or Failure) can safely be extracted.

How does Scala Future work?

Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.

What is the difference between a Java Future and a Scala Future?

A Java Future works in a synchronous blocking way. It does not work in an asynchronous non-blocking way, whereas a Scala Future works in an asynchronous non-blocking way. If we want an asynchronous non-blocking feature, we should use Java 8's CompletableFuture.


1 Answers

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.

like image 107
Rüdiger Klaehn Avatar answered Sep 30 '22 00:09

Rüdiger Klaehn