JavaScript: how the try/catch construct works with async/await

There are some peculiarities related to the use of the try/catch construct when it comes to the async/await model.

This construct is used to handle exceptions. The code within the try block is evaluated and if it raise an exception, the JavaScript interpreter passes the control to the catch block where you can inspect the error's details contained in an error object.

try {
    let result = 5 / 0;
} catch(err) {
    console.log(err);
}

Since Promises usually return a custom error object when they are rejected, catch will show you the details of the rejection error.

'use strict';

const isEven = num => {
   return new Promise((resolve, reject) => {
        if(typeof num !== 'number') {
                reject({error: 'Not A Number'});
        }
        if( num % 2 === 0 ) {
            resolve(true);
        } else {
            reject({error: 'Odd Number'});
        }
   });
};

const test = async () => {
    try {
        let result = await isEven(Math.floor(Math.random() * 10));
        console.log(result);
    } catch(err) {
        console.log(err);
    }
};

In this case our Promise will return a truthy value in case of an even number and a custom error object either when the number is odd or is not a number. The catch block will simply handle the Promise's rejection.

Things get more complicated when in the try block we raise an unexpected error which is not related to Promises:

const test = async () => {
    try {
        let result = await isEven(2);
        let x = 5 / 0;
    } catch(err) {
        console.log(err);
    }
};

Here the error object will be an empty object ({}) with no details about the exception raised in the try block. It becomes extremely difficult to track down such errors because our first thought is that there's a problem with Promises.

However, this is not the case, because Promises are normally designed to be very detailed when it comes to describing the condition that led to their rejection.

Articles