While composing an async generator function, I noticed that the following construct results in a SyntaxError
:
async function * foo() {
await yield bar; // Can not use 'yield' as identifier inside a generator
}
Even though reversing the order of the contextual keywords is perfectly acceptable:
async function * foo() {
yield await bar; // OK
}
After reading the error carefully, I was able to correct the syntax by wrapping the UnaryExpression
within the AwaitExpression
in parentheses to avoid parsing the token yield
as an identifier instead of a contextual keyword:
async function * foo() {
await (yield bar); // OK
}
But this begs the question, what specific static semantics in ECMAScript 2018 are involved that cause yield
to be parsed as an identifier in this context, while await
does not require special treatment?
It's a matter of precedence of the await
operator, which forms a UnaryExpression
(and has one as its operand), unlike the yield
operator which forms an AssignmentExpression
(and has one as its optional operand). An AssignmentExpression
does not form a UnaryExpression
, which means that you simply are not allowed to nest them like that.
When the await
expression is parsed, the next token fed to the parse is used to form a UnaryExpression
, and the only choice for yield
to do that is as an IdentifierReference
(totally ignoring the bar
that follows after it). Of course in a generator parsing context that is not allowed, leading to the confusing error message.
Notice that both forms of nesting (await (yield …)
and yield (await …)
) are totally unnecessary anyway, as the yield
keyword in an asynchronous generator function already does await both the yielded value and the resumption value internally, so you should just omit the await
keyword and use only yield
.