Otaqui.com Blog

Introducing Javascript Promises (aka Futures) in Google Chrome Canary

Javascript Promises, also known as Futures or occasionally Deferred, is now available in Google Chrome Beta (behind the “enable-experimental-web-platform-features” flag).

Take a look at the WHATWG Dom Spec for Promises for the definitive api.

I’ve written about Promises before, but will take the opportunity to give another introduction.

Better than complicated callback logic

Promises provide a very clear utility of being able to attach multiple callbacks to a single async operation.

Here’s an example where two kinds of callback are attached to a single promise, one for debugging and one for actual application events:

var promise = getAjaxAndReturnAPromise();
promise.then(
  function(data){ console.debug('DEBUGGING', data); },
  function(err){ console.error('DEBUGGING ERROR', err); }
);
promise.then(
  function(data) { $(data.html).appendTo('body'); },
  function(err) { $('<div class="err">The request failed!</div>').appendTo('body'); }
);

Obviously, you could just put both things in a single pair of success / callback functions. However, the more things you have “observing” an async function, the worse that gets, particularly if you need to start adding if / else logic and the like.

More importantly, we get chaining and error handling

So far, so good – but as Domenic Denecola writes, Promises are about much more than callback aggregation .

Because a then() function should also return a promise you can now perform composition and error handling in much more the way you can with synchronous programming.

Let’s consider an example. Imagine a situation where you want to:

  1. run an ajax request for an index file with a list of resource urls;
  2. get the first url for immediate display;
  3. get second url to cache it for better performance when the user clicks “next”;
  4. stop the process and show an error to the user if either of the first two operations fail.

We’ll also imagine that we have two methods:

function getAjax(url, onsuccess, onfailure); // callback based
function getAjaxPromise(url); // returns a promise

I’ve avoided using jQuery here since it’s promise implementation isn’t quite correct, so it should be avoided. Let’s imagine that getAjaxPromise returns a “correct” implementation of then (see the article by Dominic Denecola for more information).

Callback Example

var cached_next_thing;
// operation 1, get the index
getAjax('http://example.com/things/index.json',
  // success callback for operation 1
  function(data1) {
    // operation 2, fetch the first "thing"
    getAjax(data1.things[0].url,
      // success callback for operation 2
      function(data2) {
        // display the content of first "thing"
        $(data2.thing.content).appendTo('#container');
        // operation 3: get the second "thing" in case the user clicks "next"
        getAjax(data1.things[1].url,
          // operation 3 success callback
          function(data3) {
            cached_next_thing = data3.thing.content;
          },
          // operation 3 failure callback
          function(err3) {
            // no-op
          }
      },
      // operation 2 error callback
      function(err2) {
        $('<div class="error">Could not fetch the thing</div>').appendTo('body');
      }
    );
  },
  // operation 1 error callback
  function (err1) {
    $('<div class="error">Could not fetch the list of things</div>').appendTo('body');
  }
);

Nasty, isn’t it? And this is incredibly simple logic with hardly anything happening.

Promises example

var list_of_things,
    cached_next_thing;
// operation 1
getAjaxPromise('http://example.com/things/index.json')
  .then(
    function(data) {
      list_of_things = data;
      // operation 2
      return getAjaxPromise(data.things[0].url);
    },
    function(err) {
      $('<div class="error">Could not fetch the list of things</div>').appendTo('body');
    }
  )
  .then(
    function(data) {
      $(data.thing.content).appendTo('#container');
      // operation 3
      return getAjaxPromise(list_of_things[1].url);
    },
    function(err) {
      $('<div class="error">Could not fetch the thing</div>').appendTo('body');
    }
  )
  .then(
    function(data) {
      var cached_next_thing = data.thing.content;
    },
    function(err) {
      //no-op
    }
  );

Hopefully you agree that this is much cleaner. The first thing of note is that success and failure pairs are always next to each other, rather than opposite ends of the nested callback tree.

Note that since we aren’t dealing with nested scopes, I had an extra variable so I could access the “list of things” in operation 3. The flip side is that because we don’t have nested scopes, we don’t have to work around potentially conflicting argument names (by calling things data1, data2, etc). I think that the nested scope approach might seem “nicer” in a trivial example like this one, but again – start adding much more logic, and certainly any more operations, and you are indeed in callback hell.

Conclusion

So there you have it – an example of promises compared to callbacks, which shows that even with a simple example you get immediate benefits of readability, and that with more complex examples you can compose and handle errors in a much more sensible way than with nested callbacks.