Forgive me for the Typos and Grammatical Mistakes, I'm still learning. 🙏
What are Promises?
Promises are a way to handle asynchronous operations in JavaScript. They represent a value that may be available now, or in the future, or never. Promises have three states: pending
, fulfilled
, and rejected
.
Types of Promises
Pending: The initial state of a promise. It represents that the operation is still in progress and has not been completed yet.
Fulfilled: The state of a promise when the operation has been completed successfully. The promise has a value, and it is available to be used.
Rejected: The state of a promise when the operation has failed. The promise has a reason for the failure, and it can be handled using the
catch
method.
Why Promises are Important?
- Promises help in writing cleaner and more readable asynchronous code.
- They provide a way to handle asynchronous operations in a more structured manner.
- Promises can be chained together to perform multiple asynchronous operations sequentially.
- Whether fetching data, handling multiple tasks or racing for the fast result, Promises are essential in Modern JavaScript.
1. Simple Promise
const promise = new Promise((resolve, reject) => {
// Imagine fetching user data from an API
const user = {
name: "Aasim Ashraf",
age: 21,
};
user ? resolve(user) : reject("User not found");
});
promise
.then((user) => console.log(user))
.catch((error) => console.log(error));
A Promise that either resolves
or rejects
often used for API calls
or async
tasks.
- When to Use : For single async operation like fetching data from an API.
- Advantages : Clean handling of success and failure in one block.
2. Promise.all Multiple Operations
const fetchUser = fetch("/users").then((res) => res.json());
const fetchPosts = fetch("/posts").then((res) => res.json());
Promise.all([fetchUser, fetchPosts])
.then(([user, posts]) => {
console.log(user, posts);
})
.catch((error) => console.log(error));
Waits for all promises to resolve, if one fails, the whole chain fails. Best for multiple async tasks that needs to be resolved together.
- When to Use : For multiple async operations that are not dependent on each other.
- Advantages : Fetch multiple data at once and handle them together.
- Disadvantages : If one fails, all fail.
What Happens if One Promise Fails in Promise.all?
const fetchUser = fetch("/users").then((res) => res.json());
const fetchPosts = fetch("/posts").then((res) => res.json());
Promise.all([fetchUser, fetchPosts])
.then(([user, posts]) => {
console.log(user, posts);
})
.catch((error) => console.log(error));
Problem with Promise.all
is that if one promise fails, the whole chain fails. To avoid this, you can use Promise.allSettled
.
3. Promise.allSettled
const fetchUser = fetch("/users").then((res) => res.json());
const fetchPosts = fetch("/posts").then((res) => res.json());
Promise.allSettled([fetchUser, fetchPosts])
.then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("User Data:", result.value);
} else {
console.log("Error:", result.reason);
}
});
});
Promise.allSettled
waits for all promises to settle, whether they are resolved or rejected. It returns an array of objects with a status
and value
or reason
.
- When to Use : When you want to know all results, even failures.
- Advantages : Fetch multiple data at once and handle them together.
- Disadvantages : If one fails, it won't stop the chain
4. Promise.race Fastest Result
const fast = new Promise(resolve => setTimeout(resolve, 1000, "Fast"));
const slow = new Promise(resolve => setTimeout(resolve, 2000, "Slow"));
Promise.race([fast, slow])
.then((result) => {
console.log(result);
})
.catch((error) => console.log(error));
Returns the result of the first promise to settle, whether it's resolved or rejected. Useful when you need speed, such as loading the first available response.
- When to Use : When speed matters more than waiting for all results.
- Limit : You may get an error if the fastest promise fails.
What if a Promise in Promise.race Fails?
const error = new Promise((resolve) => {
setTimeout(() => resolve("Error"), 1000);
});
const success = new Promise((resolve) => {
setTimeout(() => resolve("Success"), 2000);
});
Promise.race([error, success])
.then((result) => {
console.log(result);
})
.catch((error) => console.log("First Rejected",error));
If the first promise fails, the whole chain fails. To avoid this, you can use Promise.any
.
5. Promise.any First Successful Result
const promise1 = Promise.reject("Error 1");
const promise2 = new Promise(resolve => setTimeout(resolve, 3000, "Promise 2"));
Promise.any([promise1, promise2])
.then((result) => {
console.log("First Success",result);
})
.catch((error) => console.log("All Rejected",error));
Resolves when any one Promise is resolved. Ignores all rejections until all promises are rejected. Useful when you need the first successful result, regardless of the rest.
- When to Use : When you need the first successful result, regardless of the rest of the promises.
- Limit : If all promises are rejected, it will throw an error.
Recap
- Simple Promise: For single async operation like fetching data from an API.
- Promise.all: For multiple async operations that are not dependent on each other.
- Promise.allSettled: When you want to know all results, even failures.
- Promise.race: When speed matters more than waiting for all results.
- Promise.any: When you need the first successful result, regardless of the rest of the promises.
Final Thoughts
- Choosing the right type of promise is the key to efficient asynchronous programming.
- Use the Promise that best fits your use case: Speed, multiple operations, or handling all results.
Top comments (3)
good, thanks
You are Welcome.
Here is my practical example on using Promise.race to add timeouts to fetch calls.