JavaScript: Error handling with Promises and Async/Await

gitfudge - Oct 11 '19 - - Dev Community

Error handling in JavaScript can be easy while being tricky at certain places, especially Promises. JS allows error handling with the help of try, catch, and throw.

const main = () => {
  try {
    // Do something super crazy
    if (!bakePizza()) {
      throw new Error('Oh no!');
    }
  } catch (error) {
    // That escalated quickly
    handleOvenExplosion();
  }
}
Enter fullscreen mode Exit fullscreen mode

This seems simple enough but gets a little tricky when Promises are involved.

Let’s look at a simple example of a Promise. The following Promise function fetches a list of user profiles from the database, where the result set is resolved by the promise function and the error is rejected.

const userProfileQuery = new Promise((resolve, reject) => {
  connection.query('SELECT * FROM Users', [], (err, result) => {
    if (err) reject({ type: 'SQL', err});
    connection.release();
    resolve(result);
});
userProfileQuery
  .then((data) => {
    const userList = data;
    // Do something crazy with the list
  })
  .catch((err) => {
    // Oh, snap!
    // Handle error
  });
Enter fullscreen mode Exit fullscreen mode

In an ideal world, we’d want to have a single try-catch block to handle all errors that occur in that single file.

const { getUserProfiles } = require('./helpers');
module.exports = () => {
  try {
    let userProfileList;
    getUserProfiles
      .then((data) => {
        userProfileList = data;
      })
      .catch((error) => {
        // Handle Promise Error
        // All errors thrown in this promise land here
      });
  } catch (error) {
    // Handle errors in this module
  }
}
Enter fullscreen mode Exit fullscreen mode

The above module is simple — It fetches a list of user profiles with the help of a Promise function.

But the problem with the above module is that when we throw a new Error inside the then block of the promise, it will always pass to the catch block of the promise. That is because throwing a new error inside a then block of a promise will always be passed to the catch block of the invoking promise function. This does not allow us to handle all errors in a module with a singular try-catch block.

But, alas! There is a way to handle this with the help of Async/Await. Let me explain this better with an example —

const { getUserProfiles } = require('./helpers');
module.exports = async () => {
  try {
    const userProfileList = await getUserProfiles;
  } catch (error) {
    // Handle errors in this module
    switch (type) {
      case ERROR_SQL:
        // Handle SQL errors
      default:
        // Handle common errors
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This little addition of async/await in your code does two things —
Assign the value to the variable which was resolved by the promise function.

Throw error if the promise function rejects anything.
Note that the value assignment works only when a promise functions resolves some value and errors get thrown only when the promise function rejects something.

This way, async/await lets us keep our code clean, maintainable, and easy to read.


Thanks for reading. If you have thoughts on this, be sure to leave a comment.

. . . .
Terabox Video Player