Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve promise inside async generator #23887

Merged
merged 5 commits into from
May 9, 2018
Merged

Resolve promise inside async generator #23887

merged 5 commits into from
May 9, 2018

Conversation

agentcooper
Copy link
Contributor

Fixes #23858, #23731.

@@ -906,7 +906,14 @@ namespace ts {
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function step(r) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the spec, yield will always await. We already handle this behavior for yield* via __asyncDelegator. Rather than changing the helper here, we could handle this by changing the emit for the async generator body to replace yield x with yield yield __await(x).

Copy link
Contributor Author

@agentcooper agentcooper May 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rbuckton makes sense! I gave it a try and updated the PR. Could you take another look?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yield yield __await looks to me like you're rewriting yield x into yield await x. I'm not sure those are the right semantics, even if they appear to work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kovensky, they actually are the correct semantics per the spec, specifically in 25.5.3.7 AsyncGeneratorYield:

  1. Set value to ? Await(value).

return setOriginalNode(
setTextRange(
createYield(
createDownlevelAwait(node.expression)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With any transformation you need to ensure you continue visiting the subtree, so this should be createDownlevelAwait(visitNode(node.expression, visitor, isExpression))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rbuckton done

);
}

if (node.expression && node.expression.kind !== SyntaxKind.AwaitExpression) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual ECMAScript specs don't care whether you are already awaiting the operand to yield, e.g. in yield await x, you await twice: once for the await and once for the yield. It also doesn't matter if yield has an expression. Rather, yield is treated as yield undefined, and as a result we still have to await undefined before we actually yield.

It seems inefficient, but the runtime semantics would be observably different. If we don't have the same number of underling Awaits as the spec, then you could end up with observable differences in resolution timing between our downlevel emit and uplevel ECMAScript runtime behavior for the same code.

@DanielRosenwasser, @bterlson: Any thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agentcooper: in practice this would mean replacing line 151 with node.expression ? visitNode(node.expression, visitor, isExpression) : createVoidZero() and removing the if condition on line 146.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rbuckton got it. should I go ahead with the change or are we waiting for others to reply?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, go ahead and make the change.

@rbuckton rbuckton merged commit f17bf54 into microsoft:master May 9, 2018
@rbuckton
Copy link
Member

rbuckton commented May 9, 2018

Looks good, thanks for the contribution!

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants