Paper, Coffee, Pencil


Promises, promises, promises

This article was originally published on the Programlabbet Blog
2013-12-25.

Javascript is a fantastic little language, filled to the brim with
warts and gems alike. One of the definitive gems is its asynchronous
nature, but the flip-side wart is the reliance of callbacks to deal
with it.

To combat the complexity that arises when you nest callbacks within
callbacks, a number of patterns and solutions have emerged, one being
promises.

Promises is a way of structuring up asynchronous Javascript code in a
manner that makes it easy to follow and control the flow of execution.


So what is it, then? A promise is an object that represents an eventual
result or failure of a function. In its simplest form, it has only one
method, then, that accepts the function to call once the promise has
been fullfilled. In a slightly more useful form, there's also a method
fail that registers an error callback.

To illustrate, let's say that we have a computation which will be ready
after an indeterminate period. We pass a callback that will be called
once the result is available and go on our Merry Way:

    var resultLater = function (callback) {
      setTimeout(function () {
        callback(1 + 1);
      }, Math.floor(Math.random() * 1000));
    };

    resultLater(function (result) {
      console.log(result);
    });

Of course, since we now have invoked the asynchronous beast, we have no
guarantee when or where we will get the result, unless our Merry Way is
contained within the callback function. This is the entryway to
callback hell, a boring and silly place. (You may think that you can
escape by emitting and reacting to events instead of nestling functions.
Do this and you have monstrous can of spaghetti to deal with.
Bon app├ętit!)

Callback hell isn't our only problem. The code above is guaranteed to
result in no error. But how do we deal with errors, and especially,
exceptions? Easy, we do this by adding another callback:

    var resultLater2 = function (givenValue, callback, errback) {
      setTimeout(function () {
        try {
          callback(1 / Math.floor(Math.random() * givenValue));
        }
        catch (err) {
          errback(err);
        }
      }, Math.floor(Math.random() * 1000));
    };

    resultLater2(123,
                 function (result) { console.log(result); },
                 function (err)    { console.error(err); });

Neat! Now we can handle the possibility of errors.
However, this doesn't do us any good in the callback hell. In fact,
things has just gotten worse, since if every asynchronous call follow
this pattern, we need at the very least handle the error of each
subsequent call. At worst, we have diverging paths for the cases where
an error is deemed "recoverable". Disaster!

It's about here promises make an entrance. Since a promise is an object
that hold the eventual result or failure of a computation, we can also
use it to escape callback hell through method chaining:

    var resultLater3 = function (givenValue) {
      var callbacks = [],
          errbacks  = [];

      setTimeout(function () {
        var result,
            next;

        try {
          result = 1 / Math.floor(Math.random() * givenValue),
          next   = null;

          callbacks.forEach(function (callback) {
            next = callback(next || result);
          });
        }
        catch (err) {
          result = err,
          next   = null;

          errbacks.forEach(function (errback) {
            next = errback(next || result);
          });
        }
      }, Math.floor(Math.random() * 1000));

      return {
        then : function (callback) {
          callbacks.push(callback);
          return this;
        },
        fail : function (errback) {
          errbacks.push(errback);
          return this;
        }
      };
    };

This is of course a very limited example, but it presents the gist of
the idea. So what have we done here? We now have a way of chaining
together functions with then that depend on the return value of the
previous call all the way up to the fullfilled promise. We also have
a way of handling the unfullfilled promise in a similar manner with
fail.

    resultLater3(123)
      .then(function (result) { return result + 1; })
      .then(function (result) { return result * 5; })
      .then(function (result) { console.log(result); })
      .fail(function (error) { return error.stack; })
      .fail(function (stackTrace) { console.error(stackTrace); })

Now, it isn't very apparent, but we have effectively decoupled the
computation in a manner that makes it irrelevant when the steps happen,
only that we can guarantee that the functions will be called in the
order they are stated. Right now, however, we can only have simple
functions and not other promises as callbacks, but the semantics should
hold for that too. You get the idea.

There are a lot more we can do with promises that isn't covered here, of
course, but I hope atleast that this article whetted your appetite and
gave you some insights to this handy pattern. If you feel ready to try
promises out, I can warmly recommend the promises library Q by
Kristopher Michael Kowal, et al. Enjoy!


## Further reading

* Q design documents
* What's so great about JavaScript Promises?