1. Promise States
A Promise has three states
- Pending: Initial state (neither fulfilled nor rejected)
- Fulfilled: The operation completed successfully
- Rejected: The operation failed
2. Creating a Promise
Use the Promise constructor with resolve and reject callbacks
const promise = new Promise((resolve, reject) => {
// Async operation (e.g., API call, setTimeout)
const success = true;
if (success) {
resolve("Operation succeeded!");
} else {
reject("Operation failed!");
}
});
3. Consuming Promises
Use .then(), .catch(), and .finally() to handle outcomes
promise
.then((result) => {
console.log(result); // "Operation succeeded!"
})
.catch((error) => {
console.error(error); // Runs if rejected
})
.finally(() => {
console.log("Always runs"); // Cleanup tasks
});
4. Chaining Promises
Chain .then() calls to sequence async operations
function getUser() {
return new Promise((resolve) => {
setTimeout(() => resolve({ id: 1, name: "Alice" }), 1000);
});
}
function getPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve(["Post 1", "Post 2"]), 500);
});
}
getUser()
.then((user) => getPosts(user.id)) // Pass user.id to next step
.then((posts) => console.log(posts)) // ["Post 1", "Post 2"]
.catch((error) => console.error(error));
5. Static Methods
Handle multiple Promises concurrently
Promise.all()
Resolves when all Promises resolve; rejects if any reject
const p1 = Promise.resolve(3);
const p2 = fetch("data.json");
const p3 = new Promise((resolve) => setTimeout(resolve, 100, "foo"));
Promise.all([p1, p2, p3])
.then((values) => console.log(values)) // Array of results
.catch((error) => console.error(error));
Promise.race()
Resolves/rejects as soon as one Promise settles
const p1 = new Promise((resolve) => setTimeout(resolve, 500, "Slow"));
const p2 = new Promise((resolve) => setTimeout(resolve, 200, "Fast"));
Promise.race([p1, p2])
.then((value) => console.log(value)); // "Fast"
Promise.allSettled()
Waits for all Promises to settle (success or failure)
Promise.allSettled([p1, p2])
.then((results) => results.forEach((result) => console.log(result.status)));
Promise.any()
Resolves when any Promise fulfills; rejects if all reject
Promise.any([p1, p2])
.then((value) => console.log(value));
6. Async/Await Syntax
Write Promise-based code synchronously using async/await
async function fetchData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error);
}
}
fetchData();
7. Why Promises? The Callback Hell Problem
Imagine ordering pizza where every step (order → bake → deliver) requires shouting through a window. That’s callback hell!
// CALLBACK HELL
getUser(userId, (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
console.log(comments); // Finally!
}, errorCallback);
}, errorCallback);
}, errorCallback);
// PROMISE CHAIN
getUser(userId)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(error => handleError(error)); // Single error handler
8. Common Pitfalls
- Uncaught Errors: Always include .catch().
- Return Values: Forgot to return in .then()? The next .then() gets undefined.
- Parallel Execution: Use Promise.all() instead of sequential awaits for efficiency.
async function getUserPosts(userId) {
try {
const user = await fetch('/users/userId');
const posts = await fetch('/users/userId/posts');
return { user: user.json(), posts: posts.json() };
} catch (error) {
console.error("Failed to load:", error);
throw error; // Re-throw for caller to handle
}
}
getUserPosts(1)
.then((data) => console.log(data))
.catch((error) => console.error("Critical error:", error));