Ah, JavaScript errors… The uninvited guests of our code.
You’re happily coding your Node.js application, everything seems perfect, and then... BOOM!
UnhandledPromiseRejectionWarning: Error: Something went wrong!
What just happened?!
Don’t worry! In this guide, you’ll learn:
- Why error handling in async code is tricky.
- How to gracefully handle errors in callbacks, Promises, and Async/Await.
- Best practices to keep your app from exploding unexpectedly.
Grab some, and let’s fix these errors!
The Problem: Errors in Asynchronous Code
Error handling in synchronous code is easy:
try {
let result = 10 / 0;
console.log(result); // Infinity, but no crash!
} catch (error) {
console.log("Oops! Error:", error);
}
But in asynchronous code, things get messy.
setTimeout(() => {
throw new Error("Oops! Something went wrong!");
}, 1000);
This will crash your entire app!
Handling Errors in Callbacks (a.k.a. The Old Way)
Before Promises, we handled errors using error-first callbacks:
const readFile = (filename, callback) => {
setTimeout(() => {
let error = Math.random() > 0.5 ? new Error("File not found!") : null;
let data = error ? null : "File content here...";
callback(error, data);
}, 1000);
};
// Using error-first callback
readFile("data.txt", (err, data) => {
if (err) {
console.log("Error:", err.message);
} else {
console.log("Success:", data);
}
});
Output (Randomly succeeds or fails):
Error: File not found!
OR
Success: File content here...
Problems with this approach:
- Too many nested callbacks lead to Callback Hell.
- Error handling spreads all over the codebase.
Solution? Use Promises!
Handling Errors in Promises (Better, but Still Not Perfect)
Promises reject when something goes wrong:
const readFilePromise = (filename) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
let error = Math.random() > 0.5 ? new Error("File not found!") : null;
error ? reject(error) : resolve("File content here...");
}, 1000);
});
};
// Handling with .catch()
readFilePromise("data.txt")
.then(data => console.log("Success:", data))
.catch(err => console.log("Error:", err.message));
Output:
Error: File not found!
OR
Success: File content here...
Key Benefits of Promises:
- No more nested callbacks!
-
Errors bubble up to
.catch()
automatically.
But there’s still a better way… Async/Await!
Handling Errors in Async/Await (The Best Way!)
Async/Await makes error handling simple with try/catch
:
async function fetchFile() {
try {
let data = await readFilePromise("data.txt");
console.log("Success:", data);
} catch (error) {
console.log("Error:", error.message);
}
}
fetchFile();
Output:
Error: File not found!
OR
Success: File content here...
Why Async/Await is the best?
- Looks like synchronous code, easy to read.
-
Handles errors cleanly with
try/catch
.
Unhandled Promise Rejections (The Silent Killers ☠️)
Biggest mistake in Promises? Not handling rejections!
readFilePromise("data.txt"); // NO .catch()
This will cause an unhandled promise rejection!
Solution: Use process.on()
to catch ALL promise rejections globally:
process.on("unhandledRejection", (reason, promise) => {
console.log("Unhandled Promise Rejection:", reason.message);
});
Best Practice: Always chain .catch()
in Promises or use try/catch
in Async/Await!
Handling Multiple Errors with Promise.all()
What happens if multiple async calls fail?
const p1 = readFilePromise("file1.txt");
const p2 = readFilePromise("file2.txt");
Promise.all([p1, p2])
.then(results => console.log("Success:", results))
.catch(error => console.log("Oops! One failed:", error.message));
If one fails, the entire Promise.all() fails!
Solution? Use Promise.allSettled()
!
Promise.allSettled([p1, p2])
.then(results => console.log("Results:", results));
Output:
Results: [
{ status: 'fulfilled', value: 'File content here...' },
{ status: 'rejected', reason: Error: File not found! }
]
Best for APIs where some requests may fail, but you still want the rest!
Graceful Error Handling (Don’t Crash Your App!)
Best practices to prevent Node.js crashes:
Use try/catch in async functions:
async function safeFetch() {
try {
let data = await readFilePromise("data.txt");
console.log("File:", data);
} catch (error) {
console.error("Handled Error:", error.message);
}
}
Catch ALL unhandled errors globally:
process.on("uncaughtException", (error) => {
console.error("Uncaught Exception:", error.message);
});
Catch ALL unhandled promise rejections globally:
process.on("unhandledRejection", (reason) => {
console.error("Unhandled Rejection:", reason.message);
});
Use logging tools like Winston or Sentry for monitoring!
Conclusion
Now you’re an error-handling pro in Node.js!
- You know how to handle errors in callbacks, Promises, and Async/Await.
- You understand unhandled promise rejections and how to catch them.
-
You can use
Promise.allSettled()
to handle multiple async calls safely. - You’ve learned best practices to prevent your Node.js app from crashing.
Now go write bug-free async code! Happy coding!
0 Comments