Senica Gonzalez
Senica Gonzalez

Reputation: 8182

Javascript async catch errors at caller

If I add the keyword async to a function, it seems that I have to catch errors "in" that function. Sometimes it doesn't make sense to catch errors and I want to defer them to the caller as I may not know the context in which the function is called (e.g. is the caller doing res.json(e) or next(e), or neither)

Is there a way around this? So I can use async (in order to await inside the function) and defer errors to the caller?

Here is a really contrived example

https://codepen.io/anon/pen/OzEXwM?editors=1012

try {
    function example(obj = {}) {
        try {
            obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
                //try{
                throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
                //}catch(e){
                //  console.log('I do not want to catch error here'), I wan't to defer it to the caller
                //}  
            }
            return obj
        } catch (e) {
            console.log('example outer error')
        }
    }

    let doit = example()
    doit.test() // why doesn't 'outer error' catch this?

} catch (e) {
    console.log('outer error')
}

The script ran as is will give an Uncaught Exception. But if I remove the async keyword it works (yes, I know in this example, async is silly, it's just an example)

Why can't I catch the error when doit.test() is called?

Usually, I would just comply and rework, but in trying to explain to someone else this morning, I realized, I didn't really know the answer.

Upvotes: 0

Views: 48

Answers (2)

Patrick Evans
Patrick Evans

Reputation: 42736

Why can't I catch the error when doit.test() is called?

Because it is async. By the time it has reached the throw error part the try catch block on the outside has already been executed and passed. There is nothing to throw to so to speak.

To fix this, since async and await are just syntactic sugar for Promises you just use it's catch callback. Your test() function is going to return a promise so just add a the callback onto the returned promise

doit.test().catch(()=>{ 
  console.log('Any uncaught exceptions will be sent to here now');
}); 

Demo

function example(obj = {}) {
  obj.test = async() => {
    throw new Error('example error')
  }
  return obj;
}

let doit = example()
doit.test().catch(e => {
  console.log('Caught an exception: ', e.message);
});

Upvotes: 1

Kent V
Kent V

Reputation: 563

If you want to catch error of test() function. You need to do await doit.test()

https://jsfiddle.net/1tgqvwof/

I wrapped your code in a anonymous function since await must be within async function

(async function () {
    try {
        async function example(obj = {}) {
            try {
                obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
                    //try{
                    throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
                    //}catch(e){
                    //  console.log('I do not want to catch error here'), I wan't to defer it to the caller
                    //}
                }
                return obj
            } catch (e) {
                console.log('example outer error')
            }
        }

        let doit = example()
        await doit.test() // why doesn't 'outer error' catch this?

    } catch (e) {
        console.log('outer error')
    }
})();

Upvotes: 1

Related Questions