Promises as Values

I was recently working on a small JavaScript project in which I needed to perform several asynchronous operations sequentially. I had a collection of images on a page and I needed to fade each of them out one at a time. I ended up with a nice technique using Promises.

Let's suppose we have a function that fades out an image given a selector and a duration (I was using jQuery, but the details don't matter). We can call this function like so:

fadeOut("#my-image", 1000, function () {  
  // do stuff after the fade out
});

Our fadeOut function also takes a callback. The callback will be called after the the image has been faded out.

Now suppose we have an array of image selectors that we want to fade out one by one.

var images = [...];

images.forEach(function (image) {  
  fadeOut(image, 1000, function () {
    // do stuff
  });
});

This won't work. Our fadeOut function will return immediately on each iteration even though the callback hasn't yet fired. The user will experience all images fading out simultaneously.

To solve this, let's first create a wrapper function for fadeOut that returns a Promise.

var fade = function (selector) {  
  return new Promise(function (resolve, reject) {
    fadeOut(selector, 1000, function () {
      resolve();
    });
  });
};

With this simple wrapper function we can use JavaScript's built in reduce to perform our fades in sequence.

var images = [...];

images.reduce(function (promise, image) {  
  return promise.then(function () {
    return fade(image);
  });
}, Promise.resolve());

Promises are values. This means that they can be set to our accumulator on each iteration of the reduction. We start with a resolved Promise and, one by one, fade out each of our images. Note, though, that each image only fades after the Promise in the accumulator is resolved. This means that each image will not fade until the image before it has finished fading.

I was happy to find that I could use many of the same techniques with Promises that I use with other types of values.