then, catch, and finally
Promises are consumed with three main methods:
.then().catch().finally()
These methods let you respond to success, failure, and cleanup.
.then()
.then() runs when a Promise fulfills.
Promise.resolve("Alice").then((name) => {
console.log(name); // Alice
});The callback receives the fulfilled value.
fetchUser().then((user) => {
console.log(user.name);
});Returning From .then()
The value returned from .then() becomes the value for the next .then().
Promise.resolve(2)
.then((number) => number * 2)
.then((number) => {
console.log(number); // 4
});If you return a Promise, the chain waits for it.
getUser()
.then((user) => {
return getOrders(user.id);
})
.then((orders) => {
console.log(orders);
});This is one of the most important promise rules.
.catch()
.catch() runs when a Promise rejects.
Promise.reject(new Error("Failed")).catch((error) => {
console.log(error.message); // Failed
});.catch() can handle rejections from earlier in the chain.
getUser()
.then((user) => getOrders(user.id))
.then((orders) => getOrderDetails(orders[0].id))
.catch((error) => {
console.log("Something failed:", error.message);
});Throwing Inside .then()
If a .then() callback throws, the chain becomes rejected.
Promise.resolve("data")
.then(() => {
throw new Error("Processing failed");
})
.catch((error) => {
console.log(error.message); // Processing failed
});This gives promise chains consistent error behavior.
Recovering in .catch()
If .catch() returns a value, the next .then() receives it.
Promise.reject(new Error("Failed"))
.catch(() => {
return "Fallback";
})
.then((value) => {
console.log(value); // Fallback
});This is useful when a fallback is safe.
If you cannot recover, rethrow.
loadConfig()
.catch((error) => {
throw new Error(`Could not load config: ${error.message}`);
});.finally()
.finally() runs after the Promise settles, whether fulfilled or rejected.
loadData()
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error.message);
})
.finally(() => {
console.log("Finished");
});Use .finally() for cleanup:
- hide loading spinners
- close temporary UI
- reset state
- log completion
.finally() Does Not Receive the Value
Promise.resolve("Done")
.finally((value) => {
console.log(value); // undefined
})
.then((value) => {
console.log(value); // Done
});.finally() is for cleanup, not transforming values.
Order Matters
Promise.reject(new Error("Failed"))
.finally(() => {
console.log("Cleanup");
})
.catch((error) => {
console.log(error.message);
});Output:
Cleanup
Failed.finally() runs, then the rejection continues to .catch().
Best Practices
Use .then() for fulfilled values.
Use .catch() for rejected promises.
Return values or promises from .then() when chaining.
Use .finally() for cleanup only.
Rethrow from .catch() if the error is not handled.
Avoid swallowing errors accidentally.
Common Mistakes
Mistake 1: Forgetting to Return in .then()
getUser()
.then((user) => {
getOrders(user.id);
})
.then((orders) => {
console.log(orders); // undefined
});Return the Promise:
getUser()
.then((user) => getOrders(user.id))
.then((orders) => console.log(orders));Mistake 2: Using .finally() to Transform Data
Use .then() to transform data.
Use .finally() for cleanup.
Mistake 3: Catching and Hiding Every Error
.catch(() => {})This makes debugging harder.
Handle or rethrow intentionally.
Summary
Promise methods control success, failure, and cleanup.
.then()handles fulfilled values.- Returned values flow to the next
.then(). - Returned promises are awaited by the chain.
.catch()handles rejections.- Throwing inside
.then()rejects the chain. .finally()runs after success or failure..finally()is for cleanup, not transformation.