How to know when all Promises are Resolved in a dynamic “iterable” parameter?

You can make a neat little recursive function to wrap Promise.all to handle additions to the original promise:

/**
 * Returns a Promise that resolves to an array of inputs, like Promise.all.
 *
 * If additional unresolved promises are added to the passed-in iterable or
 * array, the returned Promise will additionally wait for those, as long as
 * they are added before the final promise in the iterable can resolve.
 */
function iterablePromise(iterable) {
  return Promise.all(iterable).then(function(resolvedIterable) {
    if (iterable.length != resolvedIterable.length) {
      // The list of promises or values changed. Return a new Promise.
      // The original promise won't resolve until the new one does.
      return iterablePromise(iterable);
    }
    // The list of promises or values stayed the same.
    // Return results immediately.
    return resolvedIterable;
  });
}

/* Test harness below */

function timeoutPromise(string, timeoutMs) {
  console.log("Promise created: " + string + " - " + timeoutMs + "ms");
  return new Promise(function(resolve, reject) {
    window.setTimeout(function() {
      console.log("Promise resolved: " + string + " - " + timeoutMs + "ms");
      resolve();
    }, timeoutMs);
  });
}

var list = [timeoutPromise('original', 1000)];
timeoutPromise('list adder', 200).then(function() {
  list.push(timeoutPromise('newly created promise', 2000));
});
iterablePromise(list).then(function() { console.log("All done!"); });

In ES6 with lambdas and without comments, this can be even shorter:

function iterablePromise(iterable) {
  return Promise.all(iterable).then((resolvedIterable) => {
    if (iterable.length != resolvedIterable.length) {
      return iterablePromise(iterable);
    }
    return resolvedIterable;
  });
}

Or, as Rads expressed with async/await in their answer, but as a function:

async function iterablePromise(iterable) {
  let resolvedIterable = [];
  while (iterable.length !== resolvedIterable.length) {
    resolvedIterable = await Promise.all(iterable);  // implicit "then"
  }
  return resolvedIterable;
}

Bear in mind that this only covers addition, and that it’s still a little dangerous: You need to ensure that the callback order is such that any promises in flight add themselves to the list before the Promises.all callback can be invoked.

Leave a Comment