Futurakka Volume II

In the previous post, we reviewed the features of Futures in Scala and the most important methods used in the API. In this post, we’ll review another interesting API for handling asynchrony with Scala together with the use of Futures.

Promises provide an alternative way to create Future instances. A Promise has to be fulfilled with an expected value. When the Promise would be completed writing a value on it, we can work with a Future provided by it.

Please select mode of death. Quick and painless, or slow and horrible

Usually, when we deal with futures in programming languages we reach a point where we have to manipulate futures nesting them, which produces the fear callback hell. To avoid this process, these languages count on another API called Promise, which handles asynchronous tasks. We deem them promises because we are “promised” a result at a future point in time.

We’ve seen futures are defined as a type of read-only placeholder object created for a result which doesn’t yet exist. However, with the promises, we have a writable, single-assignment container, which completes a future. This means the promises are another mechanism for creating futures like a completable future.

While Future provides an interface exclusively for querying, Promise is a companion type that allows you to complete a Future by putting a value into it. This can be done exactly once. Once a Promise has been completed, it’s not possible to change it anymore.

Promises have to be fulfilled, the future simply arrives

Hooray! I’m helping!

The Scala Promise API is an object which can be completed with a value or failed with an exception. The main functions of complete the Promise are completeWith, success and failure. Also we have other auxiliary methods as future or isCompleted.

Let’s see in detail the most relevant ones :

  • future: Future containing the value of this promise.
val p = Promise[T]()
val f = p.future
  • isCompleted: Returns whether the promise has already been completed with a value or an exception.
val p = Promise[T]()
val b = p.isCompleted
  • completeWith: completes this promise with the specified future, once that future is completed.
val f = Future { 1 }
val p = Promise[Int]()
p completeWith f
p.future onSuccess {
 case x => println(x)
}

  • success: completes the promise with a value.
  • failure: completes the promise with an exception.
val p = Promise[T]()
val f = p.future
val producer = Future {
 val r = someComputation
 if (isInvalid(r))
   p failure (new IllegalStateException)
 else {
   val q = doSomeMoreComputation(r)
   p success q
 }
}

Like so many elected officials, he promised more than he could deliver

We can understand better with one example. Let’s image Hermes is going to give Bender a beer when he gets one. For this purpose, we need to define a Future. However, Hermes can’t resolve the future until Fry deliver an order of beer, so we are going to generate the future by defining a promise. After that, we are going to register a callback attached to the future with the action we are going to execute when the future success.

 

We can see during the process we need several threads for the execution. Using the method success, the promise will write once into the future. In this case, Fry will deliver the beer box. When this happens the onSuccess callback will be ready for being resolved.

 

Once the future has been successful, a message in the console will be printed, which will be executed in another thread. Therefore Bender can finally get the beer. On the other hand, a promise can also be used to complete a future with an exception, by failing the promise, using the failure method.

 

 

Stop acting so stupid!

In my own experience, there are two important things we should be aware when we program reactive systems. The first one is to make a good design of our actor system, and the second one is to have a good knowledge in the functioning of the Future and Promise APIs, in order to apply good practices and develop clean code.

1. Don’t ask tell

As we know when we define our Actor System we can use tell or ask for communicating between actors. The problem using ask is that enforces us to handle futures, which is usually a bad pattern for inter-actors communication. For this reason, we should consider not to use ask when we define the communication between our actors. This implies we should use tell from some actors to get back a response to sender actors, besides using forward to optimize the flow of communications.

2. Avoid the use of Await

When we have no other choice than apply futures inside actors, sometimes we use the Await invoking the result function with a future and a duration. This method is blocking, which is opposite to the reactive programming, as we can read in the documentation: “it is recommended that you avoid Await whenever possible— instead favoring combinators and/or callbacks. Await’s result and ready methods will block the calling thread’s execution until they return, which will cause performance degradation, and possibly, deadlock issues.”.

We only should use Await.result for test purposes or in special cases where the business needs obligate us to use it.

 

3. Avoid closing over mutable state with futures

We should never modify mutable state that is part of an actor using high order functions that belong to Future API like map, onComplete, etc. If we update mutable vars of an actor inside of callback, it would be possible that two threads would be concurrently trying to update a var at the same time, which can lead to an inconsistent behavior.

If we close over a mutable state with a Future, we eliminate one of the best guarantees from an actor, in that, it will only ever process one message from its mailbox at a time.

4. Avoid the callback hell

When we need to apply several transformations over futures, especially inside actors is important to simplify the code using promises and for comprehension loops. Especially when we have to chain several Futures between themselves, the Promise API has useful functions for doing that.

Our crew is replaceable, your package isn’t

As we’ve seen the promises are a compliment to work with futures which allow simplifying complex behaviors where several actions have to be chained in a non-sequential way.

The main goal is to design and implement reactive systems using the Akka toolkit, integrating several systems working asynchronously. Besides, we don’t have only to do an effective code but also we must try to apply KISS principles (Keep It Simple, Stupid!) for getting the more simple, testable and elegant code. The combined use of Futures and Promises is a simple way for achieving this goal.

In the next post, we’ll see practical examples using promises and futures. We’ll explore different use cases combining them in an Akka application.

Where we’re going, we don’t need roads …


References

Images

 

 

mm

Álvaro Navarro

Over the last years I've been working as a software engineer in several projects for public and private companies, mainly developing web applications and web layered based architectures to support their development. Currently I am immersing myself in the Big Data world and it's technology stack.

More Posts