Error handling is one of the most critical aspects of working with Promises. Because asynchronous code doesn’t stop the execution of your program, an unhandled error can lead to silent failures or “unhandled promise rejection” warnings that crash Node.js processes.


1. The .catch() Method

The primary way to handle errors in a promise chain is the .catch() method. It is triggered if any promise in the chain above it rejects or throws an error.

JavaScript

fetch("https://api.example.com/data")
  .then(response => {
    if (!response.ok) {
      throw new Error("Network response was not ok"); // Manually triggering an error
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => {
    console.error("There was a problem:", error.message);
  });

2. Error Propagation (Bubbling)

Errors in promises “bubble down” the chain until they find a .catch() block. If you have multiple .then() blocks and the first one fails, JavaScript skips all subsequent .then() blocks and jumps straight to the nearest .catch().

JavaScript

stepOne()
  .then(stepTwo)   // If stepOne fails, these are skipped
  .then(stepThree) 
  .catch(err => {
    console.log("Caught error from any of the above steps:", err);
  });

3. The try...catch Block (Async/Await)

When using async/await, error handling looks like traditional synchronous code. This is often considered more readable and easier to debug.

JavaScript

async function getData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    // This catches errors from fetch, json parsing, or any code in the try block
    console.error("Async Error:", error);
  } finally {
    console.log("Request attempt finished.");
  }
}

4. Specific vs. Global Handling

Chained Rejection Handlers

You can pass a second argument to .then(successCallback, errorCallback). However, this only catches errors from the promise immediately preceding it, not from the success callback itself.

  • Recommendation: Use .catch() instead of the second argument of .then() to ensure you catch errors from the processing logic as well.

Global “Catch-All”

If a promise rejects and there is no .catch() anywhere in the code, you can listen for global events to prevent crashes:

  • Browser: window.addEventListener('unhandledrejection', event => { ... });

  • Node.js: process.on('unhandledRejection', (reason, promise) => { ... });

5. Common Pitfall: Forgetting to Return

If you are inside a .then() and you call another function that returns a promise, you must return that promise. If you don’t, the error from that function will escape the current chain.

JavaScript

// ❌ BAD
.then(user => {
  updateDatabase(user); // Error here won't be caught by the next .catch()
})

// ✅ GOOD
.then(user => {
  return updateDatabase(user); // Error will propagate correctly
})

Categorized in:

Javascript,