Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async/await native implementations

Tags:

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?

like image 377
Estus Flask Avatar asked Oct 24 '17 10:10

Estus Flask


People also ask

How is async await implemented?

Async and await are built on promises. The keyword “async” accompanies the function, indicating that it returns a promise. Within this function, the await keyword is applied to the promise being returned. The await keyword ensures that the function waits for the promise to resolve.

Is async await really asynchronous?

In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously. Code after each await expression can be thought of as existing in a .then callback.

What is the purpose of async await?

Async/Await makes it easier to write promises. The keyword 'async' before a function makes the function return a promise, always. And the keyword await is used inside async functions, which makes the program wait until the Promise resolves.


2 Answers

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 2x prevalence of async-await in jsbench with async-await vs babel regenerator vs promise.

like image 116
Karen Grigoryan Avatar answered Oct 21 '22 15:10

Karen Grigoryan


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.

like image 42
Martin Adámek Avatar answered Oct 21 '22 17:10

Martin Adámek