Fun Programming

Handling Errors in Asynchronous Code in Node.js

 

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!

 

No comments:

Post a Comment