Promise.all(), race(), and any()

Interactive examples showing how each Promise combinator settles when async tasks resolve or reject at different times.

Overview: Three Different Settlement Rules

Each method receives the same set of Promises, but it waits for a different condition.

Code:
const rules = {
  all: 'fulfills only when every Promise fulfills',
  race: 'settles with the first Promise to finish',
  any: 'fulfills when the first Promise fulfills'
};

console.table(rules);
Result:

Promise.all()

Success requires every task to fulfill. One rejection rejects the whole group.

Promise.race()

The first finished task decides the result, whether it fulfilled or rejected.

Promise.any()

The first successful task wins. It rejects only when every task rejects.

Shared Async Helper

The article uses upload and load operations. This demo uses timers with the same 50% success rule.

Code:
const upload = function (blob) {
  let time = Math.round(100 + 500 * Math.random());

  return new Promise((resolve, reject) => {
    console.log(`run ${time}ms`);

    if (Math.random() > 0.5) {
      setTimeout(resolve, time, 'promise resolved ' + time + 'ms');
    } else {
      setTimeout(reject, time, 'promise rejected ' + time + 'ms');
    }
  });
};

const load = function (url) {
  return upload(url);
};
Result:
The delay and outcome are randomized each time.
Click “Run one upload()” to see the helper settle.

Promise.all(): Every Promise Must Fulfill

With three independent 50% tasks, the success probability is 0.5 * 0.5 * 0.5 = 12.5%.

Code:
(async () => {
  try {
    let result = await Promise.all([upload(0), upload(1), upload(2)]);
    console.log(result);
  } catch (err) {
    console.error(err);
  }
})();
Result:
The group succeeds only if all three rows resolve.
Click “Run Promise.all()”.

Promise.race(): The First Finished Promise Wins

The output always corresponds to the shortest delay, whether the winning Promise fulfilled or rejected.

Code:
(async () => {
  try {
    let result = await Promise.race([load(0), load(1), load(2)]);
    console.log(result);
  } catch (err) {
    console.error(err);
  }
})();
Result:
The first row to finish decides the result.
Click “Run Promise.race()”.

Promise.any(): The First Fulfilled Promise Wins

With three independent 50% tasks, the success probability is 1 - 0.5 * 0.5 * 0.5 = 87.5%.

Code:
(async () => {
  try {
    let result = await Promise.any([load(0), load(1), load(2)]);
    console.log(result);
  } catch (err) {
    console.error(err);
  }
})();
Result:
Rejected rows are ignored unless every row rejects.
Click “Run Promise.any()”.

Promise.all(): Minimum Loading Duration

Use Promise.all() to wait for both the request and a minimum display timer.

Code:
let getUserInfo = function (user) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('Name: Zhang Xinxu'), Math.floor(400 * Math.random()));
  });
};

let showUserInfo = function (user) {
  return getUserInfo().then(info => {
    console.log('User information', info);
    return true;
  });
};

let timeout = function (delay, result) {
  return new Promise(resolve => {
    setTimeout(() => resolve(result), delay);
  });
};

const time = +new Date();

let showToast = function () {
  console.log('show loading...');
};

let hideToast = function () {
  console.log('hide loading' + (+new Date() - time));
};

showToast();

Promise.all([showUserInfo(), timeout(200)]).then(() => {
  hideToast();
});
Result:
Loading stays visible for at least 200 ms.
Loading...
Click “Run loading demo”.

Promise.race(): Delay Loading Until It Is Needed

If the request finishes within 200 ms, no loading indicator appears. Otherwise, it appears for at least 200 ms.

Code:
let time = 0;

let showToast = function () {
  time = +new Date();
  console.log('show loading...');
};

let hideToast = function () {
  console.log('hide loading' + (+new Date() - time));
};

let promiseUserInfo = showUserInfo();

Promise.race([promiseUserInfo, timeout(200)]).then((display) => {
  if (!display) {
    showToast();

    Promise.all([promiseUserInfo, timeout(200)]).then(() => {
      hideToast();
    });
  }
});
Result:
The request duration is randomized from 0 to 399 ms.
Loading...
Click “Run smart loading demo”.

Promise.race(): Cancel Work After a Timeout

Race useful work against a timeout, then cancel the work if it did not finish quickly enough.

Code:
function timeout(delay) {
  let cancel;

  const wait = new Promise(resolve => {
    const timer = setTimeout(() => resolve(false), delay);

    cancel = () => {
      clearTimeout(timer);
      resolve(true);
    };
  });

  wait.cancel = cancel;
  return wait;
}

function doWork() {
  const workFactor = Math.floor(600 * Math.random());
  const work = timeout(workFactor);

  const result = work.then(canceled => {
    if (canceled) {
      console.log('Work canceled');
    } else {
      console.log('Work done in', workFactor, 'ms');
    }

    return !canceled;
  });

  result.cancel = work.cancel;
  return result;
}

function attemptWork() {
  const work = doWork();

  return Promise.race([work, timeout(300)]).then(done => {
    if (!done) {
      work.cancel();
    }

    return done ? 'Work complete!' : 'I gave up';
  });
}

attemptWork().then(console.log);
Result:
Work has a random 0-599 ms duration and gives up at 300 ms.
Click “Attempt work”.

Promise.race(): Keep Batch Concurrency Fixed

When the active queue reaches the limit, Promise.race() waits for one task to finish before starting another.

Code:
async function batchRequests(options) {
  let nextBatch = 1;
  let promises = [];

  while (nextBatch <= options.totalBatches) {
    const batchNumber = nextBatch++;

    const promise = doLongRequestForBatch(batchNumber).then(() => {
      promises = promises.filter(p => p !== promise);
    });

    promises.push(promise);

    if (promises.length >= options.concurrentBatches) {
      await Promise.race(promises);
    }
  }

  return Promise.all(promises);
}

batchRequests({ totalBatches: 8, concurrentBatches: 3 });
Result:
Eight batches run with a concurrency limit of three.
Click “Run batch queue”.

Promise.any(): Use the Fastest Available Resource

Request the same resource through multiple paths and use the first successful response.

Code:
let startTime = +new Date();

let importUnpkg = loadResource('unpkg', 715, true);
let importJsdelivr = loadResource('jsDelivr', 83, true);

Promise.any([importUnpkg, importJsdelivr]).then(resource => {
  console.log('Loading complete. Time: ' + (+new Date() - startTime) + 'ms');
  console.log(resource.version);
});
Result:
unpkg 715 ms
jsDelivr 83 ms
Try making one CDN fail or changing the delays.
Click “Load resource”.

Compatibility: Check Promise.any() Support

Promise.any() arrived later than Promise.all() and Promise.race(), so production code may need a fallback.

Code:
const supportsPromiseAny = typeof Promise.any === 'function';

if (supportsPromiseAny) {
  console.log('Promise.any() is supported in this browser.');
} else {
  console.log('Promise.any() needs a polyfill or fallback.');
}
Result:
Checking...