Errors in Async Code
Handling errors in asynchronous code works differently than synchronous code. A regular try/catch won't catch errors inside a callback or a rejected promise. You need different strategies for each async pattern.
// This won't catch the error!
try {
setTimeout(() => {
throw new Error("Oops!");
}, 1000);
} catch (error) {
console.log("This never runs"); // ❌
}
Catching Promise Errors
For promises, use .catch() or wrap the await in a try/catch block. Unhandled promise rejections can crash your Node.js app or show warnings in the browser.
// Using .catch()
fetchData()
.then(data => console.log(data))
.catch(error => console.log("Failed:", error));
// Using try/catch with async/await
async function load() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.log("Failed:", error);
}
}
The Finally Block
The finally block runs whether the async operation succeeds or fails. It's great for cleanup tasks like hiding a loading spinner.
async function loadUser() {
showSpinner();
try {
const response = await fetch("/api/user");
const user = await response.json();
displayUser(user);
} catch (error) {
showError("Could not load user");
} finally {
hideSpinner(); // Always runs
}
}
Try it Yourself →
Unhandled Rejection Handler
You can set up a global handler for unhandled promise rejections. This acts as a safety net for errors you didn't catch elsewhere.
// Browser
window.addEventListener("unhandledrejection", (event) => {
console.error("Unhandled rejection:", event.reason);
});
// Node.js
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled rejection:", reason);
});
Summary
Always handle errors in async code. Use try/catch with async/await, .catch() with promises, and set up global handlers as a safety net. Don't let promise rejections go unhandled.