Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching errors from nested async/await functions

I have a function chain in a node 4.3 script that looks something like, callback -> promise -> async/await -> async/await -> async/await

like so:

const topLevel = (resolve, reject) => {
    const foo = doThing(data)
    .then(results => {
        resolve(results)
    })
    .catch(err => {
        reject(err)
    })
}

async function doThing(data) {
    const thing = await doAnotherThing(data)
    return thing
}

async function doAnotherThing(data) {
    const thingDone = await etcFunction(data)
    return thingDone
}

(The reason it isn't async/await all the way through is that the top level function is a task queue library, and ostensibly can't be run async/await style)

If etcFunction() throws, does the error bubble up all the way to the top-level Promise?

If not, how can I bubble-up errors? Do I need to wrap each await in a try/catch and throw from there, like so?

async function doAnotherThing(data) {
   try {
     await etcFunction(data)
   } catch(err) {
     throw err  
   }
}
like image 956
Brandon Avatar asked Nov 28 '16 16:11

Brandon


People also ask

How do I catch an error in an async function?

When an error is thrown in an async function, you can catch it with a try {} catch {}. So this works as you'd expect: This is syntax sugar for what you might have been doing with promises earlier: In fact, anywhere you use the keyword await, you can remove await and do the traditional .then () and .catch () calls.

How do I use await inside an async function?

Top level await has poor support so, in general, you can only use await inside an async function. You have to call an async function for it to do anything. The async function has to get a promise to await from somewhere and usually that is by calling another function.

How to handle thrown exceptions in async/await?

Async/await has a clean syntax, but you still have to handle thrown exceptions in async functions. Handling error with .catch in promise .then chain can be difficult unless you implement custom error classes. Using the handle utility function, we are able to avoid Unhandled promise rejection error and also handle error granularly.

How to use try / catch within async function?

This means the caller could use try / catch within an async function that await s the call to userProfile () or by using .catch () on the promise returned by userProfile (). The caller's option to not handle the error and allow the error to continue propagating normally is also preserved.


1 Answers

If etcFunction() throws, does the error bubble up all the way through the async functions?

Yes. The promise returned by the outermost function will be rejected. There's no need to do try { … } catch(e) { throw e; }, that's just as pointless as it would be in synchronous code.

… bubble up all the way to the top-level Promise?

No. Your topLevel contains multiple mistakes. If you don't return the doThing(data) from the then callback, it will be ignored (not even awaited) and the rejection stays unhandled. You'll have to use

.then(data => { return doThing(data); })
// or
.then(data => doThing(data))
// or just
.then(doThing) // recommended

And in general, your function should look like this:

function toplevel(onsuccess, onerror) {
    makePromise()
    .then(doThing)
    .then(onsuccess, onerror);
}

No unnecessary function expressions, no .then(…).catch(…) antipattern (that could lead to onsuccess and onerror to both be called).

like image 200
Bergi Avatar answered Sep 30 '22 11:09

Bergi