
We all know that parallel and concurrent applications are need of the hour. To write these applications, multithreading is used. Thread safety is very common problem when working on such applications. So, Futures gives an easy way to run one or more tasks concurrently in Scala. When we create a new future, Scala creates a new thread and executes its code. The result of the computation is assigned to a future when threads completes its execution. In other words, Future is an abstraction to represent the completion of an asynchronous operation.
In order to use future, an implicit execution context is required. It controls the whole thread pool in which future executes. To run any code we need to import the below 2 lines :-
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
Callback
A better approach for working with scala futures is to use callbacks method. The most common callback methods are : –
- onComplete :- When a future completes(even if got failed), this callback method is used to execute a certain set of statements. It allows to do something with the Future result but it does not return any value. To extract inner result, we can use pattern matching.
- map :- Map can also be used which will implement its assigned function if future succeeds.
- flatMap :- Sometimes, there are situations where we need to pass one future as input to another input operation. Then, the return type will be nested future(like Future[Future[Int]]). To return non-nested Future, it is used.
Apart from this, Scala also provides await object for blocking a thread until the future executes. But it will limit the advantages of multithreading and can create a deadlock scenario. So its use is highly discouraged.
Handling Failures
A future runs in different thread from which it is defined. That’s why, we cannot handle the exceptions in normal try catch block. Below mentioned 2 functions can we used to handle the same:
- recover :- It is used to handle particular exception with a different value. It takes a partial function that turns any matching exception into a successful result.
- recoverWith :- To handle a particular exception with another exception, recoverWith method is used instead of recover.
Sample Code
import scala.concurrent.{Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import scala.util.Random
object Demo extends App {
println("starting Execution ...")
def divide(x:Int, y:Int) :Future[Int]=Future{
Thread.sleep(Random.nextInt(100))
x/y
}
val f = Future {
Thread.sleep(Random.nextInt(200))
0
}
//usuage of onComplete
f.onComplete {
case Success(value) => println(s"Got the callback value = $value")
case Failure(e) => e.printStackTrace
}
//using recover for handling failure with
divide(4,2).map{ result => println(result)
}.recover {case ex: Exception => println("Exception found :$ex")}
println("1 ..."); Thread.sleep(150)
println("2 ..."); Thread.sleep(150)
Thread.sleep(2000)
}
Thanks for reading it.