Executing Promises in Sequence (and Stopping at the First Resolved Promise)

Here's a challenge that a friend of mine came across yesterday. He needed to iterate over an array of file names one at a time in a specific order, stopping at the first one that existed. He was, of course, using Node.js and the checks were being done asynchronously.

The Problem

You might think this would be easy, especially if you're using a promise library such as Bluebird, but it's not as simple as it sounds. Since promises execute in parallel, there's no guarantee of which one will resolve first.

To do what my friend needed to do, we had to wait for each promise to complete before moving on to the next one (i.e. run them in sequence).

The Solution

The solution I came up with uses recursion to iterate over an array of values. It passes them one by one to an async function of your choice. It runs in sequence and stops on the first resolved promise.

function firstInSequence(values, asyncFn) {
  return new Promise(function(resolve, reject) {
    // Are there any values to check?
    if(values.length === 0) {
      // All were rejected
      reject();
    }
    // Try the first value
    asyncFn(values[0]).then(function(val) {
      // Resolved, we're all done
      resolve(val);
    }).catch(function() {
      // Rejected, remove the first item from the array and recursively
      // try the next one
      values.shift();
      firstInSequence(values, asyncFn).then(resolve).catch(reject);
    });
  });
}
// Some async function... function yourAsyncFunction(val) { return new Promise(function(resolve, reject) { // Brief pause to show sequence setTimeout(function() { if(val >= 3) { resolve(val); } else { reject(val); } }, 500); }); }
// Example usage firstInSequence([1, 2, 3, 4, 5], yourAsyncFunction).then(function(val) { console.log('Resolved on ' + val); }).catch(function() { console.log('All were rejected'); });

While I appreciate the asynchronous nature of Node.js, sometimes the world needs a bit of order.

What do you think? Do you have a more elegant approach?

Author avatar

About the author

New Hampshirite building web apps in Florida. Creator of Surreal CMS, Postleaf, and DirtyMarkup.

Need to get in touch? Catch me on Twitter.