Published 2013-06-03.
Last modified 2014-11-13.
Time to read: 4 minutes.
Composable futures are a powerful concurrency mechanism – the one you should consider first if you need non-blocking, asynchronous computations. This lecture features a tiny implementation of a WriteOnce
storage class, Promise
and Future
.
This lecture, and most of the other lectures in this section, is updated and expanded material which was originally published in my book, Composable Futures with Akka 2.0. That book is now out of date; it only pertained to Scala 2.10.
The sample code for this lecture can be found in
courseNotes/
.
A Future
is a container which will assume a value when a computation completes or a result becomes available at a future time.
This lecture pertains to the Future
class and companion object in the scala.concurrent
package.
Scala Future
s:
- Manage multi-threading asynchronously (unlike Scala parallel collections, which are synchronous).
- Are higher-level than j.u.c. primitives.
- Are immutable.
- Are inexpensive to create.
- Can be preset to a value or exception when created.
- Are non-blocking, however blocking operations are supported.
- Are composable.
Methods that return a Future
instead of blocking for a result provide fast, asynchronous, non-blocking code.
These methods are called asynchronous.
A Future Can Start With a Promise
A Future[T]
starts an asynchronous computation on another execution context and makes the result
available to the original execution context once it has completed.
Future
s are writable once and readable many times.
They are usually created by passing in a closure (discussed in the
Closures lecture of the
Introduction to Scala course) or a Function
(discussed in the Functions are First Class and
More Fun With Functions lectures of the same course),
so they usually start off without a value.
The two states of a Future
are pending or completed.
The value of a Future[T]
might be of type T
or it might be an Exception
.
This is possible because the internally, a Future
’s value is stored in an instance of Try[T]
.

Future
s are executed as soon as an execution context is available to perform the work.
Normally when you hear “another execution context”, you think “another thread”,
but there are situations where there is only one thread ever available,
so for single-threaded programs like those produced by NodeJS, many execution contexts are interleaved on one thread.
This is good when you do not have hardware support for multi-threading, but is quite inefficient otherwise.
In many cases where multi-threading is enabled, a Future
will start computing the value on another
thread immediately, but you cannot assume this to always be the case, because another thread may not be available right away.
A Promise
is a signaling mechanism which exists in the same execution context as it was created on.
A Promise
can be created complete with a value or exception,
or it can be completed with a value or exception at later time.
Unlike Future
s, promises are fulfilled on demand.
The value of a Promise
is held by an internal Future
.
Internally, a Promise
stores the value more or less as type Future[Try[T]]
.
We will explore the storage mechanism in more detail in a moment.
This means that Promise
s also have two states, just like Future
s: pending or completed.
When complete, a Promise
might have a Success[T]
value, or it might contain a Throwable
.
-
Invoking
Promise.complete(newValue)
triggers a transition from statePending
toCompleted
while storingSuccess(newValue)
as the value of thePromise
. -
Invoking
Promise.failure(throwable)
triggers a transition from statePending
toCompleted
while storingFailure(throwable)
as the value of thePromise
.
This might seem confusing, so let’s examine a simplified implementation of futures so we can understand how this really works.
I only show you this code so you can see how Promise
relates to Future
, and how value storage works.
Although TinyPromise
and the write-once mechanism are reasonable facsimiles of how the real Scala implementation works,
the TinyFuture
implementation is so minimal that it could teach bad habits.
Beware!
WriteOnce Immutable Type
One of the main concepts behind Scala futures is a write-once variable that can store one of two types of information: a parametric value or a Throwable
.
I’ve defined the WriteOnce
class that is faithful to the documented behavior of Future
s and Promise
s.
The following code is provided in TinyFuture.scala
and is defined in a TinyFuture
object
.
Once you get familiar with this code, understanding Scala’s Future
s and Promise
s will be easy.
If you need some help remembering what the exists
combinator does,
review the Combinators lecture which was presented earlier in this course.
class WriteOnce[T] { private var value: Option[Try[T]] = None
def isCompleted = value.isDefined
def isFailure = value.exists(_.isFailure)
def isSuccess = value.exists(_.isSuccess)
/** Create an instance with a failure value (a Throwable instance) */ def fail(throwable: Throwable): Unit = { if (value.isEmpty) value = Some(Failure(throwable)) else throw new Exception(WriteOnce.alreadySetMSg) () }
/** Create an instance with a success value */ def set(newValue: T): T = { if (value.isEmpty) value = Some(Success(newValue)) else throw new Exception(WriteOnce.alreadySetMSg) newValue }
/** Get success value */ def success: T = value.map { case v if v.isSuccess => v.get case v => throw new Exception("WriteOnce is a failure, there is no value stored.") }.getOrElse(throw new Exception(WriteOnce.stillPendingMsg))
/** Get failure value (a Throwable instance) */ def failed: Throwable = value.map { case v if v.isFailure => v.failed.get case v => throw new Exception("WriteOnce is a success, there is no Throwable stored.") }.getOrElse(throw new Exception(WriteOnce.stillPendingMsg)) }
object WriteOnce { val stillPendingMsg = "WriteOnce is still pending, there is no value yet." val alreadySetMSg = "Value of WriteOnce is already set" }
We can create a WriteOnce
instance in various ways and use them, like this:
object WriteOnceDemo extends App { import TinyFuture.WriteOnce
val writeOnce1 = new WriteOnce[Int]() writeOnce1.set(42) println(s"writeOnce1.isCompleted=${writeOnce1.isCompleted}") println(s"writeOnce1.isFailure=${writeOnce1.isFailure}") println(s"writeOnce1.isSuccess=${writeOnce1.isSuccess}") println(s"writeOnce1.success=${writeOnce1.success}") try { writeOnce1.failed } catch { case exception: Throwable => println(s"writeOnce1.failed: ${exception.getMessage}") } println()
val writeOnce2 = new WriteOnce().set(420) println(s"writeOnce2.isCompleted=${writeOnce2.isCompleted}") println(s"writeOnce2.isFailure=${writeOnce2.isFailure}") println(s"writeOnce2.isSuccess=${writeOnce2.isSuccess}") println(s"writeOnce2.success=${writeOnce2.success}") try { writeOnce2.failed } catch { case exception: Throwable => println(s"writeOnce2.failed: ${exception.getMessage}") } println()
val writeOnce3 = new WriteOnce().fail(new Exception("You reached the end of the Internet")) println(s"writeOnce3.isCompleted=${writeOnce3.isCompleted}") println(s"writeOnce3.isFailure=${writeOnce3.isFailure}") println(s"writeOnce3.isSuccess=${writeOnce3.isSuccess}") try { writeOnce3.success } catch { case exception: Throwable => println(s"writeOnce3.success: ${exception.getMessage}") } println(s"writeOnce3.failed=${writeOnce3.failed}") }
You can run this by typing:
$ sbt "runMain multi.futures.WriteOnceDemo" writeOnce1.isCompleted=true writeOnce1.isFailure=false writeOnce1.isSuccess=true writeOnce1.success=42 writeOnce1.failed: WriteOnce is a success, there is no Throwable stored.
writeOnce2.isCompleted=true writeOnce2.isFailure=false writeOnce2.isSuccess=true writeOnce2.success=420 writeOnce2.failed: WriteOnce is a success, there is no Throwable stored.
writeOnce3.isCompleted=true writeOnce3.isFailure=true writeOnce3.isSuccess=false writeOnce3.success: WriteOnce is a failure, there is no value stored. writeOnce3.failed=java.lang.Exception: You reached the end of the Internet
TinyFuture Implementation

My tiny Future
implementation is rather short and simple.
Because it does not support concurrency it just shows how the WriteOnce
behavior supports the Future.value
getter.
Getting the value of this tiny Future
implementation is very different than how you retrieve values from the
real Scala Future
implementation.
Although this code is simple and easy to understand,
it is a very naïve implementation and its only useful for showing how Futures work with WriteOnce
values.
I will show you how to extract values from real Scala Future
s later.
class Future[T] { val value = new WriteOnce[T]
def isCompleted = value.isCompleted }
object Future { def apply[T](value: => T) = { val future = new Future[T]() try { future.value.set(value) } catch { case e: Exception => future.value.fail(e) } future }
def failed[T](throwable: Throwable) = { val future = new Future[T] future.value.fail(throwable) future }
def successful[T](value: T): Future[T] = Future(value) }
The TinyFuture
companion object has two methods that are just like two methods of the real Scala
Future’s companion object of the same name:
successful
and failed
.
These methods create completed Future
s from the values passed to them; no extra threads or context switch is required.
These methods are useful when you need to supply a Future containing a value that does not require computation as an argument to a method call,
or for use in a for-comprehension.

Tiny Promise Implementation
Next is the tiny version of Promise
.
Even though it is quite simple, this code is reasonably faithful to the Scala implementation because Promise
does not require concurrency support; that is the job of Future
.
class Promise[T] { val future = new Future[T]
def complete(result: Try[T]): Promise[T] = { if (result.isSuccess) future.value.set(result.get) else future.value.fail(result.failed.get) this }
def failure(cause: Throwable): Promise[T] = { future.value.fail(cause) this }
def success(value: T): Promise[T] = { future.value.set(value) this }
final def tryCompleteWith(other: Future[T]): Promise[T] = { if (!future.isCompleted) { if (future.value.isSuccess) future.value.set(other.value.success) else future.value.fail(other.value.failed) } this }
def tryFailure(cause: Throwable): Boolean = { if (future.isCompleted) { false } else { future.value.fail(cause) true } }
def trySuccess(value: T): Boolean = { if (future.isCompleted) { false } else { future.value.set(value) true } } }
object Promise { def apply[T](): Promise[T] = new Promise[T]
def failed[T](throwable: Throwable): Promise[T] = { val promise = new Promise[T] promise.future.value.fail(throwable) promise }
def successful[T](value: T): Promise[T] = { val promise = new Promise[T] promise.future.value.set(value) promise } }

TinyFutureDemo
Once again, this demo code does NOT show you how you would obtain values from real Scala Futures
.
I only show you this code so you can see how WriteOnce
and Promise
relate to Future
.
The fragments highlighted in yellow are what I want you to notice;
the fragments highighted in black are NOT how you should work with futures or promises,
they are just there for this quick little demo.
Don’t pick up these bad habits.
object TinyFutureDemo extends App { import TinyFuture._
val promise1 = Promise.successful(1) println(s"Value of promise1 = ${promise1.future.value.success}")
val promise2 = Promise.failed(new Exception("The knurblefritz has been badly gurbled")) println(s"Throwable of promise2 = ${promise2.future.value.failed}")
val future1 = Future.successful(3+5) println(s"value of future1 = ${future1.value.success}")
val future2 = Future.failed(new Exception("The omdedomdom has been hoomified")) println(s"Throwable of future2 = ${future2.value.failed.getMessage}") }
You can run this by typing:
$ sbt "runMain multi.futures.TinyFutureDemo" Value of promise1 = 1 Throwable of promise2 = java.lang.Exception: The knurblefritz has been badly gurbled value of future1 = 8 Throwable of future2 = The omdedomdom has been hoomified
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.