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

Make async iterator return() and next() more like async generators #891

Merged
merged 3 commits into from
Jun 11, 2020

Conversation

domenic
Copy link
Member

@domenic domenic commented Jun 2, 2020

Previously, we would only set the async iterator's "ongoing promise" to null when the async iterator next steps succeeded. This meant that calling return() after a failed next() would not call the async iterator return steps, thus potentially preventing the cleanup of any associated resources. This changes the spec to reset the ongoing promise after the async iterator next steps complete, regardless of whether they succeed or fail.

Credit to @TimothyGu for finding this in jsdom/webidl2js#224 (comment). (Timothy, please review!)

Tests for this are added in the Streams context in various commits of web-platform-tests/wpt#22982.


Preview | Diff

Previously, we would only set the async iterator's "ongoing promise" to null when the async iterator next steps succeeded. This meant that calling return() after a failed next() would not call the async iterator return steps, thus potentially preventing the cleanup of any associated resources. This changes the spec to reset the ongoing promise after the async iterator next steps complete, regardless of whether they succeed or fail.
@domenic domenic requested review from annevk and EdgarChen June 4, 2020 21:09
1. Let |rejectSteps| be the following steps, given |reason|:
1. Set |object|'s [=default asynchronous iterator object/ongoing promise=] to
null.
1. [=ECMAScript/Throw=] |reason|.
Copy link
Member

Choose a reason for hiding this comment

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

A few more things to think about.

try {
  const a = await it.next();  // eventually rejected
} catch {
  const b = await it.next();
}

Async generators (and also sync generators) would set b to { value: undefined, done: true }. We could do the same by just setting the is finished flag to true, which would give us the invariant that getting the next iteration result will never be called after it has rejected a promise, which is something I think spec authors could come to expect.

Alternatively, we could consider a “one bad apple spoils the bunch” semantic, where b gets rejected. I think we should stick with the async generator behavior though.


Going further, consider this case:

const a = it.next();  // note: no await here
const b = it.next();

It seems like b should be settled in the same way as b above, as the timing of when it.next() is called shouldn't have any bearing on its result. But then this would require us to keep some unsettled promise set on object's target, which brings about additional complexity. Async generators has something analogous through the [[AsyncGeneratorQueue]] internal slot; see spec.

Copy link
Member Author

Choose a reason for hiding this comment

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

I like these ideas, and I will work to align the spec with generators in the way you suggest. It's good that we're getting this review coverage now that these things are being used seriously in specs, and implemented (albeit only implemented in webidl2js so far).

Copy link
Member Author

Choose a reason for hiding this comment

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

I've attempted to implement semantics that pass these test cases in the latest commit of jsdom/webidl2js#224. If you agree that the changes there are reasonable, I'll port them over to spec text form.

Copy link
Member Author

@domenic domenic Jun 5, 2020

Choose a reason for hiding this comment

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

Also feel free to peruse the tests added in web-platform-tests/wpt#22982 (commits)

@domenic domenic changed the title Make async iterator return() function after failed next Make async iterator return() and next() more like async generators Jun 8, 2020
@domenic
Copy link
Member Author

domenic commented Jun 8, 2020

Alright, I've ported over the changes from the jsdom/webidl2js implementation, so this should be ready for re-review. I've updated the title and original post to reflect the broader scope of this PR; feel free to critique that as well in case I missed a detail or got something wrong.

Copy link
Member

@TimothyGu TimothyGu left a comment

Choose a reason for hiding this comment

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

LGTM other than the one tiny thing.

index.bs Outdated
Comment on lines 12611 to 12612
1. Let |result| be [$CreateIterResultObject$](<emu-val>undefined</emu-val>,
<emu-val>true</emu-val>).
Copy link
Member

Choose a reason for hiding this comment

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

The webidl2js implementation uses value as the first parameter rather than undefined, I believe correctly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yep, and that's what async generators do. Great catch; thank you!

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

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

I'll rubberstamp for Timothy. And going forward that won't be needed anymore as he has commit access now. (And should probably be listed as co-editor?)

@domenic domenic merged commit 590fdc5 into master Jun 11, 2020
@domenic domenic deleted the async-iterator-return-after-next branch June 11, 2020 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants