Reputation: 2201
I try to follow the ECMAScript spec to find out how async/await
do its work and encountered some hurdle.
Let's suppose there is such code:
function boo() {
return 1;
}
async function foo() {
let result = await boo();
return result;
}
foo();
So let's start to track step by step what must be done under the hood:
Running [[Call]]
internal method of foo()
, that suspends the current execution context (callerContext
), creates new one (calleeContext
) and pushes it onto the execution context stack. We have the execution context stack of this look: callerContext < calleeContext -- running
.
The next step is a foo()
evaluation (that is AsyncFunctionBody
associated with it), that in effect is EvaluateAsyncFunctionBody
. This routine, in turn, calls AsyncFunctionStart
. It creates a copy of the running execution context - asyncContext
- and pushes it onto the stack, which now is as follows: callerContext < calleeContext < asyncContext -- running
. calleeContext
is left anxious to receive the result of asyncContext
evaluation later (step 10).
As it was prescribed in AsyncFunctionStart
, asyncContext
evaluates FunctionBody
of foo()
. The await
expression is met along the way. It is defined to get the result of boo()
firstly and subsequently call Await
routine with 1
as an argument.
Await
creates a promise resolved with 1
and performs its then()
, so that the promise can notify of its fulfilled value and this value would exposed as the result of the await
expression. But for now asyncContext
is removed from the execution context stack, which at the current time is callerContext < calleeContext -- running
.
It is difficult to me to understand what is next due to happen. A promise takes the opportunity to notify its subscribers only when the execution context stack is empty because for the sake of it the Job
queue is involved. In the other hand, caleeContext
is waiting for the result of asyncContext
evaluation (step 10 of AsyncFunctionStart
) and I can't find the place where both execution contexts (calleeContext
and callerContext
) removed from the stack. It seems like a deadlock!
Upvotes: 3
Views: 155
Reputation: 664538
caleeContext
is waiting for the result ofasyncContext
evaluation
Not really "waiting". As you said, the await
keyword removes the asyncContext
from the call stack, and returns1 undefined
in step 6 of AsyncFunctionStart2. After this is done, step 5 of EvaluateAsyncFunctionBody does return the promise result. With this result, step 7 of [[Call]] is done, and step 8 does "Remove calleeContext
from the execution context stack and restore callerContext
as the running execution context.". Now foo()
has returned the promise.
The callerContext
is finally removed from the stack in step 14 of ScriptEvaluation - it's the global scriptContext
.
1: See also the NOTE (step 15) in Await: "This returns to the evaluation of the operation that had most previously resumed evaluation of asyncContext
."
2: See also the assertions (step 7 and 8) in AsyncFunctionStart: "When we return here, asyncContext
has already been removed from the execution context stack and runningContext
is the currently running execution context. result
is a normal completion with a value of undefined
. [A] possible source of completion values [is] Await".
Upvotes: 1