Why does exception still get thrown after catch in async function?

Viewed 1732

When an exception is caught, the rest of the code still execute.

function bad(){
    //throw new Error('I am an exception')
    return Promise.reject("I am an exception")
}

(
 async function (){
      let  msg = await bad().catch( e=>console.log("This is an error: " + e))
      console.log("I want to stop before executing this line ")
 }
)()

I'm trying to catch the exception in this way instead of try and catch

My first issue is how to stop the code from reaching the rest of the code after the error is caught.

The second issue is, if I replaced the Promise reject with the commented out throw error, the exception error thrown won't be caught, why is that ?

1 Answers

Because you're converting rejection to fulfillment with your catch handler. The await will await the promise returned by catch, which never rejects. So there's no reason the code wouldn't continue, since await only sees a fulfilled (never rejected) promise.

Remember that catch returns a new promise that will settle based on what the rejection handler does. Your rejection handler suppresses the error by effectively returning undefined, thus fulfilling the promise.

It's very much as though you did this:

async function (){
    let msg;
    try {
        msg = await bad();
    } catch (e) {
        console.log("This is an error: " + e);
    }
    console.log("I want to stop before executing this line ")
}

In a comment you've asked:

So there is no way other than including all my code inside the try block, and only use the one-liner .catch() if I have no more code to follow ?

Just to get it out of the way: Usually, the best thing is not to handle errors at all in any function other than the top-level entry point to the code (e.g., functions called at the top level of your code or by event handlers). Handling errors too soon is a classic antipattern. Allow them to propagate to the caller where they can be handled in a centralized fashion.

But in situations where it's appropriate to handle the error at this level, yes, try/catch is probably your best bet with your following logic in the try block:

async function() {
     try {
         let msg = await bad();
         console.log("I want to stop before executing this line ")
     } catch (e) {
         console.log("This is an error: " + e);
     }
}

It also has the advantage of not having a variable declared in the containing scope that you're not going to use. If the "one liner" thing is important to you, there's no much difference at all between the .catch and the catch:

async function() {
     try {
         let msg = await bad();
         console.log("I want to stop before executing this line ")
     } catch (e) { console.log("This is an error: " + e); }
}

Or you could return a flag value of some kind:

async function() {
     let msg = await bad().catch(e => { console.log("This is an error: " + e); return null; });
     if (msg === null) {
         return;
     }
     console.log("I want to stop before executing this line ")
}

But frankly, that's really swimming against the current. :-)

Related