Why is it possible to try-catch an async-await call?

后端 未结 2 2010
时光说笑
时光说笑 2021-01-13 22:30

There is a common anti pattern in JavaScript:

function handleDataClb(err, data) {
    if(!data) throw new Error(\'no data found\');
    // handle data... 
}          


        
相关标签:
2条回答
  • 2021-01-13 22:54

    async funtions

    An async function returns a promise that is resolved by a value returned by the function body, or rejected by an error thrown in the body.

    The await operator returns the value of a fulfilled promise or throws an error, using the rejection reason, if the awaited promise is rejected.

    Errors thrown by await can be caught by try-catch blocks inside the async function instead of allowing them to propagate up the execution stack and rejecting the promise returned by calling the async function.

    The await operator also stores the execution context before returning to the event loop, in order to allow promise operations to proceed. When internally notified of settlement of the awaited promise, it restores the execution context before proceeding.

    A try/catch block set up in the async function's execution context is not altered or rendered ineffective simply because the context has been saved and restored by await.

    As an aside

    "async-await is implemented using generators, promises, and coroutines"

    may be part of how Babel transpiles async function and await operator usage, but native implementations could be implemented more directly.


    Generator functions (Update)

    A generator function's execution context is stored in its associated generator object's internal [[Generator Context]] slot. (ECMA 2015 25.3.2)

    Yield expressions remove the generator's execution context from the top of the execution context stack (25.3.3.5 of ES6/ECMAScript 2015)

    Resuming a generator function restores the the function's execution context from the generator object's [[Generator Context]] slot.

    Hence generator functions effectively restore the previous execution context when a yield expression returns.

    Throwing an error within a generator function for normal reasons (syntax error, a throw statement, calling a function which throws) can be caught by a try-catch block as expected.

    Throwing an error by means of Generator.prototype.throw() throws an error within the generator function, originating from the yield expression that last passed control from the generator function. This error can be trapped by try-catch as for ordinary errors. (Refs MDN using throw(), ECMA 2015 25.3.3.4

    Summary

    Try-catch blocks around yield statments used in await transpilation code work for the same reason they do around await operators within native async functions - they are defined in the same execution context as the error is thrown for a rejected promise.

    0 讨论(0)
  • 2021-01-13 23:00

    Why and how does the catch work? How does the coroutine aka async manage to throw the error inside the try-catch?

    A yield or await expression can have 3 different outcomes:

    • It can evaluate like a plain expression, to the result value of that
    • It can evaluate like a throw statement, causing an exception
    • It can evaluate like a return statement, causing only finally statements to be evaluated before ending the function

    On a suspended generator, this can be achieved by calling either the .next(), .throw() or .return() methods. (Of course there's also a 4th possible outcome, to never get resumed).

    …when one of the asyncTask calls results in a promise rejection?

    The awaited value will be Promise.resolve()d to a promise, then the .then() method gets invoked on it with two callbacks: when the promise fulfills, the coroutine is resumed with a normal value (the promise result), and when the promise rejects, the coroutine is resumed with an abrupt completion (exception - the rejection reason).

    You can look at the co library code or the transpiler output - it literally calls gen.throw from the promise rejection callback.

    0 讨论(0)
提交回复
热议问题