Understanding Error Handling in Asynchronous JavaScript
When writing asynchronous code in JavaScript, such as using Promises
or async/await
, it's important to handle errors properly. Errors in async code behave differently compared to synchronous code, so we must be cautious and deliberate in our handling strategy.
Why Error Handling is Important in Async Code?
- Network requests can fail due to connectivity or server issues.
- Third-party APIs can return errors or unexpected data formats.
- File operations or database queries may timeout or reject.
Example 1: Error Handling in Promises using .catch()
Let’s say we have a promise that may reject due to some error. We can handle it using the .catch()
method.
function getData() {
return new Promise((resolve, reject) => {
const success = false; // Change this to true to test success
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Failed to fetch data.");
}
});
}
getData()
.then(result => console.log(result))
.catch(error => console.error("Error:", error));
Output:
Error: Failed to fetch data.
How does .catch()
work?
The .catch()
method is attached to the end of a promise chain. It catches any error that is thrown or passed to reject()
in the chain.
Question:
What happens if we forget to add .catch()
?
The error will be unhandled, and in modern environments, it might log a warning or throw a UnhandledPromiseRejectionWarning
.
Example 2: Error Handling with async/await
and try-catch
With async/await
, we can write asynchronous code in a synchronous style. To catch errors, we wrap the code in a try-catch
block.
async function fetchUserData() {
try {
const user = await getData(); // using the same getData function from above
console.log(user);
} catch (err) {
console.error("Caught inside async/await:", err);
}
}
fetchUserData();
Output:
Caught inside async/await: Failed to fetch data.
Why use try-catch with async/await?
Because await
pauses execution until the promise resolves or rejects. If it rejects, control jumps to the catch
block.
Question:
Can I mix async/await
with .catch()
?
Yes, although it is not common. Usually, you'd prefer try-catch
with await
. But technically, you can do this:
async function fetchData() {
await getData()
.then(data => console.log(data))
.catch(err => console.error("Mixed handling:", err));
}
fetchData();
Example 3: Global Promise Rejection Handler
Sometimes you want to catch unhandled promise rejections globally, especially in larger apps.
process.on("unhandledRejection", (reason, promise) => {
console.log("Unhandled Rejection at:", promise, "reason:", reason);
});
This is useful in Node.js environments for logging or cleanup.
Best Practices
- Always handle errors in your promises and async functions.
- Use
try-catch
withawait
for more readable code. - For multiple async operations, handle each individually or use
Promise.allSettled()
. - Log errors with enough context to debug later.
Conclusion
Asynchronous JavaScript is powerful, but only if we handle errors properly. Whether you use promises or async/await, error handling ensures your application behaves predictably and avoids silent failures.