Futures in JavaScript and Scala

10 Jun 2018

Futures, Promises, Deferreds

Wikipedia says that a future is a proxy for an unknown, incomplete computation. While this definition sufficies as a terse description of my current understanding of what a future is, I initially encountered the concept while using jQuery, where it is known as deferred. Below, when I use JavaScript examples, deferred and Promise will be mostly synonmous (deferreds are a jQuery specific thing). When I show Scala examples, Futures and Promises are different, as I will explain.

First Encountering Deferreds

I first encountered it as a means of addressing callback hell. It was common to see code using nested callbacks:

$.ajax('http://.../1', {
  complete: function() {
    $.ajax('http://.../2', {
      complete: function() {
        $.ajax('http://.../3', {
          complete: function() {
            $('#id').hide();
          }
        }
      }
    });
  }
});

We can rewrite the code using deferreds like so:

$.ajax('http://.../1')
  .then(function() {
    return $.ajax('http://.../2');
  })
  .then(function() {
    return $.ajax('http://.../3');
  })
  .then(function() {
    $('#id').hide();
  });

Just this benefit was enough for me to start using them, but at the time I didn’t realize some of the other benefits.

Using Promises

Promises are a construct to generalize the idea of asynchronous results. For example, let’s say we want to:

  1. Perform an XHR
  2. Hide an element with a delay
  3. Create some element on the page

The rest of the examples will use ES2017 syntax. Using jQuery:

$.ajax('http://.../1')
  .then(() => new Promise((resolve, reject) =>
      $('#id').hide(5000, () => resolve())))
  .then(() => $('body').append('<div>Hello World</div>'))

Above, we link different types of events in a general way, by using deferred. $.ajax natively returns a deferred, which supports the Promise interface.

We manually create a Promise so that we can use the Promise interface with $.hide.

We can also do cool things like delay the run of a function using a Promise by using setTimeout:

let delay = (millis) => new Promise((resolve, reject) =>
  setTimeout(millis, () => resolve()))

delay(1000)
  .then(() => $('#id').hide())

Next, when we use promises, we can unify error handling. We take the example before and add some error checking.

$.ajax('http://.../1')
  .then(() => new Promise((resolve, reject) => {
    let el = $('#id');
    if(el.length) {
      $('#id').hide(5000, () => resolve())
    }
    else {
      reject('no_such_element');
    }
  }))
  .then(() => {
    $('body').append('<div>Hello World</div>');
    // some checking
    throw 'some_error';
  })
  .catch(e => {
    switch(e) {
      case 'no_such_element':
        // handle accordingly
        break;
      case 'some_error':
        // handle accordingly
        break;
      default:
        // handle error we didn't expect
        break;
    }
  });

Using callbacks, this type of handling would be more convulted, requiring either that you have each calling function handle the exceptions of the callee, or wrap each callback in some sort of composing error handler function. This is much more straightforward.

Arguably the biggest benefit of Promises is that they are easy to compose. Let’s say we need the results of two XHRs:

let result1 = $.ajax('http://.../1')
let result2 = $.ajax('http://.../2')

Promise.all([result1, result2])
  .then((all) => {
    let [data1, data2] = all
    // do something with the data
  })

or that one request depends on the other:

$.ajax('http://.../1')
  .then((data1) =>
    $.ajax('http://.../1/' + data1.key)
      // we must combine the data here assuming we need both later
      .then((data2) => [data1, data2]))
  .then((all) => {
    let [data1, data2] = all
    // do something with the data
  })

Some other useful things you can do with promises:

// Turn a value into a `Promise`
Promise.resolve(1)

// Turn a value into a rejected promise
Promise.reject('error')

// Return the first completed promise
Promise.race([xhr1, xhr2, xhr3])

Promises along with generators also enable JavaScript’s await ... async syntax.

Scala Futures

Although Java has something called a Future, it’s not composable in the sense above. (Java 8 added CompletableFuture which is more similar to the Futures we are talking about.) Scala has one that is composable: scala.concurrent.Future. Creating a Future is similar to how it is done in JavaScript:

Future {
  /// some async computation
}

The method definition looks like:

def apply[T](body: > T)(implicit executor: ExecutionContext): Future[T]

Which roughly means that the Future.apply function or just Future takes function returning a T, and also implicit executor. There are a few things to unpack here. First, since Scala is a typed language, we should declare our types. Here, we have some generic type T. The type is tied to the return value of the function we pass to the Future function. For example, a Future { 1 } would be a Future[Int].

Scala has something called implicit values. Although implicits have many uses, when you see them in a method definition, they usually have to do with the context of the method. In this case, we have want to have an ExecutionContext whenever we create a Future this way.

Finally, what is an ExecutionContext? In JavaScript, things are single-threaded and our asynchronous model is based on events. There’s jsut a single-thread on which all events are processed as they complete. On the JVM, although there are event-based APIs like java.nio, the typical model of asynchronicity is thread-based. The ExecutionContext describes how you want to run the Future. Typically, you will run Futures on some sort of thread pool.

One of the most confusing differences for me between JavaScript and Scala is that Scala has both a Future and a Promise. When I first learned about Scala futures, I always wondered what the use of a Promise was. The key insight is that a Future represents a value which has already begun computation. There is no way to cancel it. It will attempt to be computed. For this reason, you sometimes need a way to create a Future without starting the computation. This could be because you are returning a Future to a caller, but do not want them to have any control over its execution.

class SchedulerWrapper implements Runnable {
  private val internalWorker = ???

  def addWork[T](work: () => T): Future[T] = {
    val promise = Promise[T]()
    internalWorker.schedule {
      promise.complete(Try{ work() })
    }
    promise.future
  }
}

In this example, have some sort of internal worker that we don’t have control of. It has a schedule function: def schedule(f: => Unit) that it will try to schedule to run in some way. If we want to return a Future from out addWork function, a promise will be the best way to do this.

The idea that a Scala Future is already started is important because the Future equivalents of other languages, such as C#, are more general in that they do not start automatically. C# has Tasks as part of the standard library, which do not have to immediately start. The Scalaz library provides something analagous to this.

Another big difference between Scala Futures and JavaScript Promises are how you can apply a function to the results. In JavaScript, you can do this:

let promise1 = ...
let promise2 = ...

// returns a promise containing `something(data)`
let promise3a = promise1.then((data) => something(data))

// returns a promise containing the results of `promise2`
let promise3b = promise1.then(() => promise2)

In Scala, it’s a little different. We can’t use the same method to operate both on Ts and Future[T]s.

val future1 = getFuture1
val future2 = getFuture2

val future3a = future1.map(data => something(data))
val future3b = future1.flatMap(_ => future2)

In Scala, we can use an alternative syntax called a for-comprehension that some people may find more readable.

val future1 = getFuture1
val future2 = getFuture2

val future3a = for {
                 data <- future1
               } yield something(data)

val future3b = for {
                 _ <- future1
                 result2 <- future2
               } yield result2

Note that these Scala examples above are different than something like

val future3b = for {
                 _ <- getFuture1
                 result2 <- getFuture2
               } yield result2

which delays any work on retrieving result2 until the first future has completed.

Conclusion

Futures or promises, whatever it is you call them, are an interesting way to get out of callback hell. What’s more, they are a simple way to add asyncronicity to code. Although using them at first may be a bit tricky, experience with them will lead to a clear intuition about how they work. They’ll come a point where you will even know intuitively how to implement them. Especially if you are experienced with JavaScript, after some study, that language’s version should be straightfoward to implement. A good first step is to think about the semantics of then. There are two key behaviors:

  1. then maps over the result of a Promise.
  2. then will unwrap a Promise returned from the function passed.
Looking for more content? Check out other posts with the same tags: