JavaScript course menu
JavaScript Promises: then, catch, finally, Chaining and Promise.all
Learn JavaScript promises: what a promise is, pending, fulfilled and rejected states, creating promises, then, catch and finally, chaining, error handling, and running work in parallel with Promise.all, allSettled, race and any.
On this page
A promise is an object that represents a value that is not available yet, such as the response to a network request. It is a placeholder that promises "I will give you a result, or an error, later".
Promise states#
A promise is always in one of three states:
Once a promise is fulfilled or rejected (settled), it never changes again.
Using a promise: then and catch#
Functions such as fetch return promises. You attach callbacks with .then() for success and .catch() for failure. To keep the examples self-contained, this lesson uses a small function that simulates a slow request with a timer:
function getUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id === 1) resolve({ id: 1, name: "Asha" });
else reject(new Error(`User ${id} not found`));
}, 50);
});
}
getUser(1)
.then((user) => console.log("Got", user.name))
.catch((error) => console.log("Failed:", error.message));
getUser(2)
.then((user) => console.log("Got", user.name))
.catch((error) => console.log("Failed:", error.message))
.finally(() => console.log("Request 2 finished"));Got Asha
Failed: User 2 not found
Request 2 finished.then(callback)runs when the promise is fulfilled, with the value..catch(callback)runs when it is rejected, with the error..finally(callback)runs either way, which is useful for hiding a loading spinner.
Creating a promise#
new Promise(executor) takes a function that receives two functions, resolve and reject. Call one of them when the work is done:
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(30).then(() => console.log("30 ms later"));30 ms laterYou will mostly consume promises from APIs rather than create them, but wrapping a callback-based or timer-based function like this is a common need. Promise.resolve(value) and Promise.reject(error) create already-settled promises.
Chaining#
.then() returns a new promise, so steps can be chained. Whatever a then callback returns becomes the value passed to the next one; if it returns a promise, the chain waits for it:
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
wait(20, 5)
.then((n) => {
console.log("start with", n);
return n * 2; // a plain value
})
.then((n) => {
console.log("doubled", n);
return wait(20, n + 1); // a promise: the chain waits for it
})
.then((n) => console.log("finally", n));start with 5
doubled 10
finally 11Compare this flat chain with the nested "callback hell" in Callbacks.
Errors in a chain#
A rejection, or an error thrown in any then callback, skips the remaining thens and goes to the next catch:
Promise.resolve(1)
.then((n) => {
throw new Error("Something broke at step 2");
})
.then(() => console.log("never runs"))
.catch((error) => console.log("Caught:", error.message))
.then(() => console.log("The chain continues after catch"));Caught: Something broke at step 2
The chain continues after catchAlways end a chain with .catch(). An unhandled rejection is reported as an error in the browser console, and in Node.js it stops the program by default.
Running promises in parallel#
When several requests do not depend on each other, start them all at once and wait for the results together:
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
const fail = (ms, msg) => new Promise((_, reject) => setTimeout(() => reject(new Error(msg)), ms));
Promise.all([wait(30, "profile"), wait(10, "orders"), wait(20, "wishlist")])
.then((results) => console.log("all:", results));
Promise.allSettled([wait(10, "ok"), fail(20, "timeout")])
.then((results) => console.log("allSettled:", results.map((r) => r.status)));
Promise.race([wait(50, "slow"), wait(5, "fast")])
.then((winner) => console.log("race:", winner));
Promise.any([fail(5, "a"), wait(15, "first success")])
.then((value) => console.log("any:", value));race: fast
any: first success
allSettled: [ 'fulfilled', 'rejected' ]
all: [ 'profile', 'orders', 'wishlist' ]Note that Promise.all keeps results in the order you passed them, even though "orders" finished first. The lines print in the order each combined promise settled.
Practice#
Practice exercise
Write delayedSquare(n) that returns a promise resolving to n * n after 20 ms, or rejecting with an Error if n is negative. Use Promise.allSettled on [2, -1, 4] and print each result as either the value or the error message.
Show solution
function delayedSquare(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (n < 0) reject(new Error(`negative: ${n}`));
else resolve(n * n);
}, 20);
});
}
Promise.allSettled([2, -1, 4].map(delayedSquare)).then((results) => {
for (const r of results) {
console.log(r.status === "fulfilled" ? r.value : r.reason.message);
}
});4
negative: -1
16Key takeaway#
Finished this lesson?