Abhishek Gupta
Abhishek Gupta

Reputation: 1193

How scala Future allow asynchronous execution?

I am new to parallel execution and scala. I have a few questions around using the Future in scala.

I believe that the Future allows the asynchronous parallel execution. So up to my understanding in the following code the donutStock method will run on a separate thread. Official document also says that it does not block the main thread. So If the main thread is not blocked then the new child thread and the main thread should be executed in parallel.

So in the following example, I expect that as soon as the donutStock method is called the control on the main thread should go forward and then the main thread should call the second donutStock method on another thread.

However, I noticed that the second method is called only after the completion of the first call. Is my understanding of the non-blocking or asynchronous is correct? And if I wanted to execute both method call is parallel then what is the correct way to do that.

I read that we should do the async operation in the server main thread. What is the advantage of async operation in such cases

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

def donutStock(donut: String): Future[Int] =  Future {
  (1 until 100).foreach { value ⇒
    println(s"checking donut stock $donut")
  }
  10
}

donutStock("My Donut").onComplete{
  case Success(value) ⇒ println("Call 1 Completed")
  case Failure(exception) ⇒ println("Call 1 Failed")
}

donutStock("Your Donut").onComplete{
  case Success(value) ⇒ println("Call 2 Completed")
  case Failure(exception) ⇒ println("Call 2 Failed")
}

Upvotes: 1

Views: 2666

Answers (2)

Krzysztof Atłasik
Krzysztof Atłasik

Reputation: 22635

When the future is created it usually starts right away using one thread. If in your current execution context has no threads available, then it might not start your future right away, but rather wait until the thread is released.

In case there is only one thread available in your execution context it might happen, that execution of the next future will have to wait for the previous future to finish.

Usually, execution context will have more threads that one available (for example in scala's global execution context number of threads defaults to number of available threads).

In your case, the problem might be, that finishing of your first future might be so fast, that it finishes before second one starts.

You can mitigate it by introducing small delay after printing value, for example by adding Thread.sleep(10) after println(s"checking donut stock $donut").

After this change, your future's will be executing slower It might cause another problem, that since futures start in daemon threads, it might happen, that main thread will terminate before the end of execution of futures. In this case they will be terminated before calling onComplete callback.

You avoid this you can wait for both futures using Await, for example:

import scala.concurrent._
import scala.concurrent.duration._

val f1 = donutStock("My Donut").onComplete{
  case Success(value) ⇒ println("Call 1 Completed")
  case Failure(exception) ⇒ println("Call 1 Failed")
}

val f2 = donutStock("Your Donut").onComplete{
  case Success(value) ⇒ println("Call 2 Completed")
  case Failure(exception) ⇒ println("Call 2 Failed")
}

val result1 = Await.result(f1, 1 second)
val result2 = Await.result(f2, 1 second)

If we can wait for the future, what is a use case for onComplete callback? For example, it might be helpful, when we define a function returning Future and we don't want to block it using Await but we still want to execute some action when the future is completed.

For example, you could modify your donutStock as below:

def donutStock(donut: String, idx: Int): Future[Int] = {
  val f = Future {
    (1 until 100).foreach { value ⇒
      println(s"checking donut stock $donut")
    }
    10
  }

  //we don't block future, but onComplete callback will be still executed when future ends
  f.onComplete{
    case Success(value) ⇒ println(s"Call $idx Completed")
    case Failure(exception) ⇒ println(s"Call $idx Failed")
  }

  f 
}

Upvotes: 1

QuickSilver
QuickSilver

Reputation: 4045

Futures are the standard mechanism for writing multi threaded code in Scala. Whenever we create a new Future operation, Scala spawns a new thread to run that Future’s code, and after completion it executes any provided callbacks.

In order to use Futures, Scala requires us to provide an implicit execution context, which controls the thread pool in which Futures execute. We can create our own execution contexts, or use the default one which usually suffices. The default execution context is backed by Fork Join Thread Pool. From code its obvious that the example uses implicit one.

def donutStock(donut: String): Future[Int] =  Future {
  (1 until 100).foreach { value ⇒
    println(s"checking donut stock $donut")
  }
  10
}

The above enclosed code will execute in its own thread when the function donutStock(<string>) with return type as Future[Int].

Scala allows us to define callback functions, which execute upon a Future’s success or failure. In the meantime, the thread that created the Future is unblocked and may continue to execute as below,

donutStock("My Donut").onComplete{
  case Success(value) ⇒ println("Call 1 Completed")
  case Failure(exception) ⇒ println("Call 1 Failed")
}

donutStock("Your Donut").onComplete{
  case Success(value) ⇒ println("Call 2 Completed")
  case Failure(exception) ⇒ println("Call 2 Failed")
}

After the Future donutStock() completes successfully, the onComplete callback receives a Success object containing the result of as 10 .

Upvotes: 1

Related Questions