Promises
Represent future values with Promises chaining .then(), handling errors with .catch(), and running operations in parallel.
Overview
Key Points
- •A Promise has three states: pending, fulfilled, or rejected and transitions only once
- •.then(onFulfilled) registers a callback to run when the Promise resolves successfully
- •.catch(onRejected) registers a callback for rejection equivalent to .then(null, onRejected)
- •.finally(fn) runs whether the Promise fulfilled or rejected great for cleanup
- •Chaining: each .then() returns a new Promise, allowing flat sequential async steps
- •Promise.all([p1, p2]) resolves when all resolve; rejects as soon as any rejects
- •Promise.allSettled([p1, p2]) waits for all to settle regardless of outcome
- •Promise.race([p1, p2]) resolves/rejects with the first Promise to settle
Examples
Creating and consuming a Promise
function fetchUser(id) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (id <= 0) {
reject(new Error("Invalid user ID"));
return;
}
resolve({ id: id, name: "Alice" });
}, 300);
});
}
fetchUser(1)
.then(function(user) {
console.log("User:", user.name); // User: Alice
return user.name.toUpperCase();
})
.then(function(upper) {
console.log("Upper:", upper); // Upper: ALICE
})
.catch(function(err) {
console.error("Error:", err.message);
});Each .then() receives the return value of the previous .then(). Returning a value wraps it in a resolved Promise automatically.
Promise chaining for sequential async steps
function getUser(id) {
return Promise.resolve({ id, name: "Bob", teamId: 42 });
}
function getTeam(teamId) {
return Promise.resolve({ id: teamId, name: "Engineering" });
}
getUser(1)
.then(user => {
console.log("User:", user.name);
return getTeam(user.teamId); // return a Promise chain waits for it
})
.then(team => {
console.log("Team:", team.name);
})
.catch(err => console.error(err));
// Output:
// User: Bob
// Team: EngineeringWhen you return a Promise from .then(), the chain waits for that Promise to resolve before calling the next .then().
Running Promises in parallel with Promise.all
const p1 = Promise.resolve("Result A");
const p2 = new Promise(resolve => setTimeout(() => resolve("Result B"), 200));
const p3 = fetch ? fetch("https://jsonplaceholder.typicode.com/todos/1").then(r => r.json()) : Promise.resolve({ title: "Sample" });
Promise.all([p1, p2])
.then(([a, b]) => {
console.log(a); // Result A
console.log(b); // Result B
});
// Promise.allSettled doesn't short-circuit on rejection
const mixed = [
Promise.resolve("ok"),
Promise.reject(new Error("oops")),
Promise.resolve("also ok"),
];
Promise.allSettled(mixed).then(results => {
results.forEach(r => console.log(r.status, r.value ?? r.reason?.message));
});Promise.all is ideal when operations are independent and you need all results. Promise.allSettled is better when you want every result regardless of failures.
Common Mistakes
- Forgetting to return a Promise inside .then() without return, the chain does not wait for the inner async operation
- Not having a .catch() unhandled rejections cause warnings in Node.js and can crash apps
- Nesting .then() inside .then() instead of chaining recreates callback hell with Promises
- Confusing Promise.all (fails fast) with Promise.allSettled (waits for all)
Note
When a .then() callback returns a Promise, the chain automatically unwraps it you never get a Promise-inside-a-Promise. This "flattening" is what makes chaining work cleanly.
Exercises
Write JavaScript code to solve each exercise. Run it and check your output.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4