1
votes

I'm going through some Future examples. I have a Future which retrieves a list from a method. I call two different kinds of callbacks, a Foreach callback and an onComplete callback just to try them out.

  • The Foreach callback nearly every time returns the list.
  • The onComplete callback rarely returns SUCCESS even though the For callback has returned the list.
  • The onComplete callback never returns FAILURE.

Can someone please explain to me what's happening?

I understand that the callbacks do execute concurrently, and do not have an order. But if the Future is returning the list to Foreach andthe onComplete callback has executed before the Foreach and Future and it tried to get the list from an unsuccessful Future, shouldn't the onComplete callback return FAILURE?

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}

object FuturesExampleObj extends App {

  println("Using Future to retrieve list\n")
  val l: Future[List[String]] = Future {
    getList()
  }

  println("FOR CALLBACK --------------------\n")

  l foreach {
    items =>
      for(item <- items) println(s"foreach item : $item")
      println("\n")
  }

  println("onComplete CALLBACK --------------------\n")

  l onComplete {
    case Success(i) => println(s"SUCCESS : $i")
    case Failure(i) => println(s"FAILURE : $i")
  }

  def getList(): List[String] ={
    val list = ("a" :: "b" :: "c" :: "d" :: "e":: Nil)
    list
  }
}

Result Example 1 (Common)

Using Future to retrieve list

FOR CALLBACK --------------------

onComplete CALLBACK --------------------

foreach item : a
foreach item : b
foreach item : c
foreach item : d
foreach item : e



Process finished with exit code 0

Result Example 2 (uncommon)

Using Future to retrieve list

FOR CALLBACK --------------------

onComplete CALLBACK --------------------


Process finished with exit code 0

Result Example 3 (very rare)

Basically onComplete never returns SUCCESS or FAILURE. On occasion, very rarely, it will return "SUCCESS: " + list.

2

2 Answers

2
votes

It's because you have to explicitly block on the future. In your case, the main thread terminates before onComplete completion and sometimes before l foreach .. is finished.

Please add:

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._

val listF = l foreach {
  items =>
    for(item <- items) println(s"foreach item : $item")
    println("\n")
}

Await.result(listF, 5 seconds)

That way you will wait for the completion of this future.

If you want to wait for onComplete, you need to use Thread.sleep (add it after the onComplete, for example:

l onComplete {
   case Success(i) => println(s"SUCCESS : $i")
   case Failure(i) => println(s"FAILURE : $i")
}

Thread.sleep(3000)

onComplete runs on some thread in the ExecutionContext, whereas Await runs on the current thread, and blocks it until it completes or the specified timeout is exceeded. Hence onComplete is nonblocking and Await is blocking.

With onComplete we are not blocking for the result from the Future but instead, we will receive a callback for either a Success or a Failure.

Thread.sleep() is blocking our main thread so that we can see the asynchronous result from the future.

Please note that you should not use Await.result in production code, it's used for testing the output of the Future. In addition, you will most certainly not use Thread.sleep() but instead "react" to the result returned by the future.

Usually, you'll have some REST API call or another service which runs and waits for the future to complete.

1
votes

The reason for that is the thread of the App finishes before the Future finishes.

Just add Await.ready(l, Duration.Inf).value.get at the and of your program.

Do this only for testing!