Which of a generator's yield vs. promise.then() is a more* correct mental model for understanding 'await'?
Property comparison, inferred by stepping through the snippet below with a debugger:
await:
await does not pause/suspend the running async function’s execution. (The running async function ‘runs to completion’, returning a pending promise when the interpreter hits the 1st await. It’s then immediately removed from the call stack.)
await waits for the promise to settle.
await expression
wraps the rest of a function's code in a microtask.
generator-yield:
- yield pauses the running function’s execution. generator functions do not ‘run to completion’.
yield promise
does ensurepromise
has settled prior to executing remaining code.- yield does not wrap or create a microtask.
promise.then(callback):
- does not pause the running function’s execution.
- waits for promise to settle before executing callback.
- creates a microtask (callback)
//promise returning function
function foo(whoCalled) {
let p = new Promise(function(resolve, reject) {
setTimeout( () => {
console.log('resolving from setTimeout - called by: ' + whoCalled)
resolve('resolve value') }, .1)
})
return p
}
//async await
async function asyncFunc() {
await foo('async function')
//rest of running function’s code…
console.log('async function howdy')
}
//generator yield:
function* gen() {
yield foo('generator function')
//rest of running function’s code…
console.log('generator function howdy')
}
//promise.then():
function thenFunc() {
let r = foo('promise.then function').then(() => {
//rest of running function’s code…
console.log('promise.then() howdy')
})
return r
}
//main
function main() {
//async await
var a = asyncFunc()
console.log(a) //logs Promise { <pending> }
//the rest of the code following await foo() runs as a microtask runs once foo() resolves. The call stack was cleared.
//generator
var g = gen()
console.log(g) // logs Object [Generator] {}
var p = g.next().value
console.log(p) //logs Promise { <pending> }
g.next() //the rest of the code following yield running gen function's code runs. call stack was not cleared.
//promise.then()
var x = thenFunc()
console.log(x) //logs Promise { <pending> }
//the then(callback) microtask runs once foo() resolves. The call stack was cleared
}
main()
console.log('main is off the call stack - launch/startup macrotask completing. Event loop entering timer phase.')
And, going beyond this comparison, what is the accurate mental model of what await
does under the hood?
await in latest ECMAScript spec for reference: https://www.ecma-international.org/ecma-262/10.0/index.html#await
await in V8 source code: https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/builtins/builtins-async-function-gen.cc#L252