async/await native implementations

2019-01-18 22:58发布

问题:

This proposal suggests that async functions can use generator functions under the hood, although I cannot find a confirmation of this in ES2017 spec.

Moreover, when generator prototype becomes messed up in Chrome/Node.js, async functions don't seem to be affected, this suggests that GeneratorFunction isn't used by AsyncFunction, at least directly:

Object.getPrototypeOf((function * () {}).prototype).next = null;

(async () => {
    return await Promise.resolve(1);
})()
.then(console.log);

How exactly does async/await work in existing native implementations?

Are the implementations more performant than it would be possible with Promise/generator function approach that is suggested by the proposal and is usually implemented in Babel and TypeScript?

回答1:

How exactly does async/await work in existing native implementations?

If we look at the actual native implementation of async await in v8 we can clearly see both promise and generator as the obvious basis of async-await implementation, also in the parser it clearly states the generator-promise nature of desugaring async-await.

Regarding ES spec, even though the spec doesn't directly mention the actual implementation of execution context switching, it hints usage of same Perform ! Call(promiseCapability.[[Resolve]] mechanism the Promise.resolve is using. Thus subjectively hinting the possible "mechanism" to handle running execution context toggling of asyncContext.

Moreover, when generator prototype becomes messed up in Chrome/Node.js, async functions don't seem to be affected, this suggests that GeneratorFunction isn't used by AsyncFunction, at least directly:

Both generator and async functions in the runtime are descendants of the Function object, they don't inherit from one another though, that's why you don't see the committed changes.

But the actual native level implementation of specific host object or method doesn't have to necessarily be connected to runtime execution of compiled counterparts and their dependencies, the same way as you cannot alter the function's ability to be called by reassigning Function.prototype.call = () => {}, since %call% is a native level implementation.

Are the implementations more performant than it would be possible with Promise/generator function approach that is suggested by the proposal and is usually implemented in Babel and TypeScript?

It depends on js engine and its implemented compilation level optimizations and deoptimizations, but it's subject to continuous change, sometimes native implementation is slower than the 3rd party lib implementation, like it happened with es5 map, forEach vs lodash counterparts, but in most cases native level implementation is unmatched due to being one level closer to machine code. As an example here is the jsperf with 2x prevalence of async-await over regenerator used in babel.



回答2:

As far as I know, generator functions are used to imitate the behavior of async/await. When you use typescript, it will be compiled to javascript, and depending of your setup, it will compile async/await syntax to the generator implementation.

More about the compilation here: https://basarat.gitbooks.io/typescript/docs/async-await.html

So you should not worry about using them in typescript at all I think.

I guess native implementation does not use generators, it should be basically just syntax sugar for working with promises.