Monday, March 27, 2023

Using the Promise.all() and Promise.allSettled() Methods in JavaScript

Using the Promise.all() and Promise.allSettled() Methods in JavaScript

This tutorial will teach you how to use promises to wait in JavaScript.

In this tutorial, I will teach you about the Promise.all() and Promise.allSettled() methods and how you can use them to work with multiple promises.

Using the Promise.all() Method

The Promise object has three useful methods named then(), catch(), and finally() that you can use to execute callback methods when the promise has settled.

The Promise.all() method is a static method, which means that it belongs to the whole class instead of being tied to any specific instance of the class. It accepts an iterable of promises as input and returns a single Promise object.

As I mentioned earlier, the Promise.all() method returns a new Promise. This new promise will resolve to an array of values of settled promises if all the promises passed to the method have resolved successfully. This new promise will also be settled with a rejection as soon as one of the passed promises gets rejected.

All Promises Resolve Successfully

Here is an example of the Promise.all() method where all the promises resolved successfully:

1
const promise_a = new Promise((resolve) => {
2
  setTimeout(() => {
3
    resolve('Loaded Textures');
4
  }, 3000);
5
});
6
7
const promise_b = new Promise((resolve) => {
8
    setTimeout(() => {
9
      resolve('Loaded Music');
10
    }, 2000);
11
});
12
13
const promise_c = new Promise((resolve) => {
14
    setTimeout(() => {
15
      resolve('Loaded Dialogues');
16
    }, 4000);
17
});
18
19
20
const promises = [
21
  promise_a, promise_b, promise_c
22
];
23
24
console.log('Hello, Promises!');
25
26
Promise.all(promises).then((values) => {
27
  console.log(values);
28
  console.log('Start the Game!');
29
});
30
31
/* Output

32


33
19:32:06 Hello, Promises!

34
19:32:10 Array(3) [ "Loaded Textures", "Loaded Music", "Loaded Dialogues" ]

35
19:32:10 Start the Game!

36


37
*/

Our statement before the call to the Promise.all() method was logged at 19:32:06. Also, our third promise named promise_c takes the longest to settle and resolves after 4 seconds. This means that the promise returned by the call to the all() method should also take 4 seconds to resolve. We can verify that it does take 4 seconds to resolve by passing a callback function to the then() method.

Another important thing to note here is that the returned array of fulfilled values contains those values in the same order in which we passed the promises to the Promise.all() method. The promise named promise_b resolves the quickest, in 2 seconds. However, its resolved value is still in the second position in the returned array. This matches the position at which we passed the promise to the Promise.all() method.

This maintenance of order can be very helpful in certain situations. For example, let's say you're fetching information about the weather in ten different cities using ten different promises. All of them are not going to resolve at the same time, and the order in which they will be resolved isn't likely to be known beforehand. However, if you know that the data is returned in the same order in which you passed the promise, you will be able to assign it properly for later manipulation.

One Promise Rejected

Here is an example where one of the promises is rejected:

1
const promise_a = new Promise((resolve) => {
2
  setTimeout(() => {
3
    resolve('Loaded Textures');
4
  }, 3000);
5
});
6
7
const promise_b = new Promise((resolve, reject) => {
8
    setTimeout(() => {
9
      reject(new Error('Could Not Load Music'));
10
    }, 2000);
11
});
12
13
const promise_c = new Promise((resolve) => {
14
    setTimeout(() => {
15
      resolve('Loaded Dialogues');
16
    }, 4000);
17
});
18
19
20
const promises = [
21
  promise_a, promise_b, promise_c
22
];
23
24
console.log('Hello, Promises!');
25
26
Promise.all(promises).catch((error) => {
27
  console.error(error.message);
28
  console.log('Stop the Game!');
29
});
30
31
/* Output

32


33
20:03:43 Hello, Promises!

34
20:03:45 Could Not Load Music

35
20:03:45 Stop the Game!

36


37
*/

Again, our statement before the call to the all() method was logged at 20:03:43. However, our second promise promise_b settled with a rejection this time. We can see that promise_b was rejected after 2 seconds. This means that the promise returned by the all() method should also reject after 2 seconds with the same error as our promise_b. It is evident from the output that this is exactly what happened.

Usage With the await Keyword

You probably already know that the await keyword is used to wait for a promise to resolve before proceeding further. We also know that the all() method returns a single promise. This means that we can use await along with a call to the Promise.all() method.

The only thing to keep in mind is that since await is only valid inside async functions and modules, we will have to wrap our code inside an async function, as shown below:

1
function create_promise(data, duration) {
2
  return new Promise((resolve) => {
3
    setTimeout(() => {
4
      resolve(data);
5
    }, duration);
6
  });
7
}
8
9
const promise_a = create_promise("Loaded Textures", 3000);
10
const promise_b = create_promise("Loaded Music", 2000);
11
const promise_c = create_promise("Loaded Dialogue", 4000);
12
13
const my_promises = [promise_a, promise_b, promise_c];
14
15
async function result_from_promises(promises) {
16
  let loading_status = await Promise.all(promises);
17
  console.log(loading_status);
18
}
19
20
result_from_promises(my_promises);
21
22
/* Outputs

23


24
08:50:43 Hello, Promises!

25
08:50:47 Array(3) [ "Loaded Textures", "Loaded Music", "Loaded Dialogue" ]

26


27
*/

This time, we have defined a function called create_promise() that creates promises for us based on the provided data and duration. Our async result_from_promises() function uses the await keyword to wait for the promises to resolve.

Using the Promise.allSettled() Method

It makes sense to use the Promise.all() method when you only want to proceed after all the promises resolve successfully. This could be useful when you are loading resources for a game, for example.

However, let's say you are getting information about the weather in different cities. In this case, it would make sense for you to output the weather information for any cities where fetching the data was successful and output an error message where fetching the data failed.

The Promise.allSettled() method works best in this case. This method waits for all the passed promises to settle either with a resolution or with a rejection. The promise returned by this method contains an array of objects which contain information about the outcome of each promise.

1
function create_promise(city) {
2
  let random_number = Math.random();
3
  
4
  let duration = Math.floor(Math.random()*5)*1000;
5
6
  return new Promise((resolve, reject) => {
7
    if (random_number < 0.8) {
8
      setTimeout(() => {
9
        resolve(`Show weather in ${city}`);
10
      }, duration);
11
    } else {
12
      setTimeout(() => {
13
        reject(`Data unavailable for ${city}`);
14
      }, duration);
15
    }
16
  });
17
}
18
19
const promise_a = create_promise("Delhi");
20
const promise_b = create_promise("London");
21
const promise_c = create_promise("Sydney");
22
23
const my_promises = [create_promise("Delhi"), create_promise("London"), create_promise("Sydney"), create_promise("Rome"), create_promise("Las Vegas")];
24
25
async function result_from_promises(promises) {
26
  let loading_status = await Promise.allSettled(promises);
27
  console.log(loading_status);
28
}
29
30
result_from_promises(my_promises);
31
32
/* Outputs

33


34
[

35
  {

36
    "status": "fulfilled",

37
    "value": "Show weather in Delhi"

38
  },

39
  {

40
    "status": "fulfilled",

41
    "value": "Show weather in London"

42
  },

43
  {

44
    "status": "fulfilled",

45
    "value": "Show weather in Sydney"

46
  },

47
  {

48
    "status": "rejected",

49
    "reason": "Data unavailable for Rome"

50
  },

51
  {

52
    "status": "fulfilled",

53
    "value": "Show weather in Las Vegas"

54
  }

55
]

56


57
*/

As you can see, each object in our array contains a status property to let us know if the promise was fulfilled or rejected. In the case of fulfilled promises, it contains the resolved value in the value property. In the case of rejected promises, it contains the reason for rejection in the reason property.

Final Thoughts

We learned about two useful methods of the Promise class that let you work with multiple promises at once. The Promise.all() method is helpful when you want to stop waiting for other promises to settle as soon as one of them is rejected. The Promise.allSettled() method is helpful when you want to wait for all the promises to settle, regardless of their resolution or rejection status.


No comments:

Post a Comment