JavaScript Promises have become a fundamental part of asynchronous programming, providing a way to handle operations that may take time to complete. Two sets of promise methods, .all() and .allSettled(), along with .race() and .any(), offer developers powerful tools to manage multiple promises efficiently. In this blog post, we will delve into the differences and use cases of each, helping you make informed decisions when dealing with asynchronous tasks in your JavaScript projects.
1. .all() vs .allSettled(): Handling Multiple Promises
a) .all(): Resolving When All Promises Fulfill
The .all() method takes an array of promises and returns a new promise that fulfills with an array of fulfillment values when all promises in the given array have fulfilled. If any promise is rejected, the entire operation is rejected.
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.all(promisesArray)
.then((results) => {
console.log("All promises resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
In this snippet, we have three promises which resolve after 500, 800 and 1000 milliseconds. Since all of them are going to be resolved so you will see the log like this after you run this snippet:
All promises resolved: [ 'Promise 1 resolved', 'Promise 2 resolved', 'Promise 3 resolved' ]
If any of the promise rejects, it won't wait for others to resolve and will return with reason of the promise that is rejected. Look at example below to get the better idea about this:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 3 rejected"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.all(promisesArray)
.then((results) => {
console.log("All promises resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
Here, promise2 resolves after 500 seconds but promise3 rejects after 800 seconds so it will not wait for promise1 and will return a response:
At least one promise rejected: Promise 3 rejected
b) .allSettled(): Resolving Regardless of Fulfillment or Rejection
In contrast, .allSettled() returns a promise that fulfills with an array of objects representing the outcome of each promise, whether fulfilled or rejected. This is useful when you want to handle all promises, regardless of their individual outcomes.
Promise.allSettled() always returns array of objects with status key which denotes fulfilled or rejected. If a promise is fulfilled then you can get response with value key and if the promise is rejected then you can find the reason in reason key.
Look at this code:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.allSettled(promisesArray).then((results) => {
console.log("All promises settled:", results);
});
You will see a response like this:
[
{ status: 'fulfilled', value: 'Promise 1 resolved' },
{ status: 'fulfilled', value: 'Promise 2 resolved' },
{ status: 'fulfilled', value: 'Promise 3 resolved' }
]
Now we will try and reject promise2 like we did previously and see the response:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.allSettled(promisesArray).then((results) => {
console.log("All promises settled:", results);
});
Response:
[
{ status: 'fulfilled', value: 'Promise 1 resolved' },
{ status: 'rejected', reason: 'Promise 2 resolved' },
{ status: 'fulfilled', value: 'Promise 3 resolved' }
]
Use Cases:
Promise.all: Useful when you want to wait for multiple asynchronous operations to complete and need all of them to be successful before proceeding. For example, making multiple API requests in parallel and waiting for all responses.
Promise.allSettled: Useful when you want to gather the results of multiple asynchronous operations, regardless of whether they succeeded or failed. It's often used when you want to know the outcome of all promises, even if some of them are rejected.
2. .race() vs .any(): Dealing with the First Resolved Promise
a) .race(): Resolving with the First Fulfilled Promise
The .race() method returns a promise that fulfills or rejects as soon as one of the promises in the given array fulfills or rejects. This can be useful when you are only interested in the outcome of the first completed promise.
For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (resolved)
Promise 4 ==> 4 seconds (resolved)
Promise 5 ==> 5 seconds (resolved)
So, it will return us Promise 1 because it was the first one being returned.
This can be useful when you're interested in the result of the first promise to complete, regardless of whether it's a success or failure.
Lets look at this example where the first promise resolves:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.race(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
You should see a response like this:
First promise resolved: Promise 2 resolved
Lets look at this example where the first promise rejects:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_,reject) =>
setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.race(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
You should see a response like this:
At least one promise rejected: Promise 2 rejected
Note: race does not care if all are resolved or all are rejected. It will give you first settled result whether it is resolved or rejected.
b) .any(): Resolving with the First Fulfilled Promise (Ignoring Rejections)
Introduced in ECMAScript 2021, the .any() method is similar to .race(), but it only considers fulfillment, ignoring rejections. It resolves with the value of the first fulfilled promise, or rejects only if all promises are rejected.
For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (resolved)
Promise 4 ==> 4 seconds (resolved)
Promise 5 ==> 5 seconds (resolved)
So, it will return us Promise 3 because it was the first one being resolved.
If all promises are rejected, it will give you an aggregated error.
For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (rejected)
Promise 4 ==> 4 seconds (rejected)
Promise 5 ==> 5 seconds (rejected)
So, now it will return an aggregated result like this:
[AggregateError: All promises were rejected]
Lets look at first example:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.any(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
You will find a similar response as race method:
First promise resolved: Promise 2 resolved
Lets look at another example:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_,reject) =>
setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800)
);
const promisesArray = [promise1, promise2, promise3];
If you run this snippet in which the promise with shortest delay promise2 of 500ms is rejected and promise3 which has next shortest delay 800ms then:
race will return promise2 with rejected reason
any will return promise3 with resolved result.
Run it with promise.any:
Promise.any(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
You will get this response:
First promise resolved: Promise 3 resolved
Run it with promise.race:
Promise.race(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
You will get this response:
At least one promise rejected: Promise 2 rejected
Below is the case of all promises being rejected with promise.any:
const promise1 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 1 rejected"), 1000)
);
const promise2 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 3 rejected"), 800)
);
const promisesArray = [promise1, promise2, promise3];
Promise.any(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
Then you will see a response like this:
At least one promise rejected: [AggregateError: All promises were rejected] {
[errors]: [ 'Promise 1 rejected', 'Promise 2 rejected', 'Promise 3 rejected' ]
}
In contrast, the same code with race method will return this:
At least one promise rejected: Promise 2 rejected
Use Cases:
Promise.race: Useful when you want to implement scenarios like a timeout mechanism where you want to respond to the first promise to complete, regardless of whether it succeeds or fails.
Promise.any: Useful when you want to handle the case where at least one promise out of multiple promises succeeds, and you're interested in the result of the first resolving promise.
Conclusion:
Understanding the distinctions between .all(), .allSettled(), .race(), and .any() allows developers to choose the most suitable method for their specific use cases. Whether you need to wait for all promises to settle, handle all promises regardless of outcome, or focus on the first resolved promise, these methods offer flexibility and control in managing asynchronous operations in JavaScript. Keep these tools in mind to enhance the efficiency and reliability of your asynchronous code.
Top comments (0)