javascriptsyntaxlanguage-lawyergeneratorecmascript-2018

Can not use 'yield' as identifier inside a generator


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?


Solution

  • 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.