- Published on
Futures in Concurrent Programming (with examples in Scala)
- Authors
- Name
- Benton Li
I will finish it in the future, but it’s no promise.
— Benton Li
Overview
In the previous blog, we see some usage of Promise
in JavaScript. However, the formal definition of a promise is different from JS’s Promise
but is more similar to JS’s Deferred
, which is less commonly used. My bold claim is that a promise is an extension or wrapper of a future. That is, you can actually implement a promise using a future. In Java, promise is known as CompletableFuture
. In Scala, a Future
is embedded in a Promise
.
Future
According to the Future
trait in Scala (source code),
a future represents a value which may or may not currently be available, but will be available at some point, or an exception if that value could not be made available.
Sounds pretty similar to the Promise
in JavaScript, right? Let’s revisit our previous example. Say, your girlfriend wants you to make an omelet for her. This is a future (technically this is a request). At some point in the future, either an omelet is available, or something goes wrong (exception). Here, the egg being cooked is the value and your omelet pan is the future. Let’s turn this into codes (try them in Scala REPL!)
import scala.util.Random
import scala.concurrent.Future
import concurrent.ExecutionContext.Implicits.global
val pan = Future[String] {
val rand = new Random()
val eggOk = Random.nextBoolean()
Thread.sleep(1000)
eggOk match {
case true => "omelet"
case false => throw new Exception("charcoal")
}
}
// val pan: scala.concurrent.Future[String] = Future(<not completed>)
Wait for a second and see what’s in the omelet pan
pan
/**
* val res: scala.concurrent.Future[String] = Future(Success(omelet))
* or
* val res: scala.concurrent.Future[String] = Future(Failure(java.lang.Exception: charcoal))
*/
Of course you can add handlers to this future
pan.onComplete {
case Success(egg) => println(s"Yummy ${egg}")
case Failure(e) => println(s"Ayo you are gonna poison me with ${e.getMessage}?")
}
Seems that this onComplete
is like a combination of then
and catch
in JS!
onComplete
Note on n.b. There were onSuccess
and onFailure
in previous versions of Scala. But they are now deprecated. Let’s look at their function signatures.
onSuccess[U](pf: PartialFunction[Nothing, U])(implicit executor: ExecutionContext): Unit
onFailure[U](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit
onComplete[U](f: Try[Nothing] => U)(implicit executor: ExecutionContext): Unit
The good news is that, instead of writing two partial callback functions, now you only need to write a single callback. Here, Try[T]
is equivalent to Either[Throwable, T]
The source code of Future.scala is here
ExecutionContext
In the first snippet, we import concurrent.ExecutionContext.Implicits.global
and the onComplete function has a parameter implicit executor
of ExecutionContext
type.
Ah, this is some crap that no one needs to worry about in when writing Promise in JS.
ExecutionContext
is a virtual place where computations take place asynchronously. It is very similar to the ThreadPoolExecutor
in Python.
ExecutionContext.global
in Scala is essentially a ForkJoinPool
. We use global
for illustration purpose only. Depending on the scenario, you might want to choose or even implement an ExecutionContext
to use resources optimally.
For example, when you do matrix multiplication, which is likely to take a longtime, you can put them in a Future
to prevent blocking. But matrix multiplication is just a series of multiplication and additions. You can split the calculations into chunks, and run chunks on multiple cores in parallel.
Just imagine you are making multiple omelets. A kitchen with multiple stoves is probably desirable.