Responding to Multiple Promises

Up to this point, each example in this chapter has dealt with responding to one promise at a time. Sometimes, however, you’ll want to monitor the progress of multiple promises in order to determine the next action. ECMAScript 6 provides two methods that monitor multiple promises: Promise.all() and Promise.race().

The Promise.all() Method

The Promise.all() method accepts a single argument, which is an iterable (such as an array) of promises to monitor, and returns a promise that is resolved only when every promise in the iterable is resolved. The returned promise is fulfilled when every promise in the iterable is fulfilled, as in this example:

  1. let p1 = new Promise(function(resolve, reject) {
  2. resolve(42);
  3. });
  4. let p2 = new Promise(function(resolve, reject) {
  5. resolve(43);
  6. });
  7. let p3 = new Promise(function(resolve, reject) {
  8. resolve(44);
  9. });
  10. let p4 = Promise.all([p1, p2, p3]);
  11. p4.then(function(value) {
  12. console.log(Array.isArray(value)); // true
  13. console.log(value[0]); // 42
  14. console.log(value[1]); // 43
  15. console.log(value[2]); // 44
  16. });

Each promise here resolves with a number. The call to Promise.all() creates promise p4, which is ultimately fulfilled when promises p1, p2, and p3 are fulfilled. The result passed to the fulfillment handler for p4 is an array containing each resolved value: 42, 43, and 44. The values are stored in the order the promises were passed to Promise.all, so you can match promise results to the promises that resolved to them.

If any promise passed to Promise.all() is rejected, the returned promise is immediately rejected without waiting for the other promises to complete:

  1. let p1 = new Promise(function(resolve, reject) {
  2. resolve(42);
  3. });
  4. let p2 = new Promise(function(resolve, reject) {
  5. reject(43);
  6. });
  7. let p3 = new Promise(function(resolve, reject) {
  8. resolve(44);
  9. });
  10. let p4 = Promise.all([p1, p2, p3]);
  11. p4.catch(function(value) {
  12. console.log(Array.isArray(value)) // false
  13. console.log(value); // 43
  14. });

In this example, p2 is rejected with a value of 43. The rejection handler for p4 is called immediately without waiting for p1 or p3 to finish executing (They do still finish executing; p4 just doesn’t wait.)

The rejection handler always receives a single value rather than an array, and the value is the rejection value from the promise that was rejected. In this case, the rejection handler is passed 43 to reflect the rejection from p2.

The Promise.race() Method

The Promise.race() method provides a slightly different take on monitoring multiple promises. This method also accepts an iterable of promises to monitor and returns a promise, but the returned promise is settled as soon as the first promise is settled. Instead of waiting for all promises to be fulfilled like the Promise.all() method, the Promise.race() method returns an appropriate promise as soon as any promise in the array is fulfilled. For example:

  1. let p1 = Promise.resolve(42);
  2. let p2 = new Promise(function(resolve, reject) {
  3. resolve(43);
  4. });
  5. let p3 = new Promise(function(resolve, reject) {
  6. resolve(44);
  7. });
  8. let p4 = Promise.race([p1, p2, p3]);
  9. p4.then(function(value) {
  10. console.log(value); // 42
  11. });

In this code, p1 is created as a fulfilled promise while the others schedule jobs. The fulfillment handler for p4 is then called with the value of 42 and ignores the other promises. The promises passed to Promise.race() are truly in a race to see which is settled first. If the first promise to settle is fulfilled, then the returned promise is fulfilled; if the first promise to settle is rejected, then the returned promise is rejected. Here’s an example with a rejection:

  1. let p1 = new Promise(function(resolve, reject) {
  2. setTimeout(function() {
  3. resolve(42);
  4. }, 100);
  5. });
  6. let p2 = new Promise(function(resolve, reject) {
  7. reject(43);
  8. });
  9. let p3 = new Promise(function(resolve, reject) {
  10. setTimeout(function() {
  11. resolve(44);
  12. }, 50);
  13. });
  14. let p4 = Promise.race([p1, p2, p3]);
  15. p4.catch(function(value) {
  16. console.log(value); // 43
  17. });

Here, both p1 and p3 use setTimeout() (available in both Node.js and web browsers) to delay promise fulfillment. The result is that p4 is rejected because p2 is rejected before either p1 or p3 is resolved. Even though p1 and p3 are eventually fulfilled, those results are ignored because they occur after p2 is rejected.