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

Implement async iterables #224

Merged
merged 11 commits into from
Jun 11, 2020
Merged

Implement async iterables #224

merged 11 commits into from
Jun 11, 2020

Conversation

domenic
Copy link
Member

@domenic domenic commented May 18, 2020

This starts to implement async iterables, #215, by following the spec as much as posisble.

This is missing:

  • Passing arguments to the utils.asyncIteratorInit hook. The hard part here is that I'm not sure how to implement convert arguments for an async iterator method, since all our code to implement conversions and handle argument processing seems to be inside generateOverloadConversions which is pretty complicated.This wasn't so bad.

  • Adding the return method appropriately. This is tricky because the Web IDL spec says to only add the return method "If an asynchronous iterator return algorithm is defined for the interface". That is, it's unclear how to generate this code without asking the runtime question of utils.asyncIteratorReturn in Impl.prototype. The best idea I have here is to add a new extended attribute, [WebIDL2JSHasAsyncIteratorReturnSteps].

I would appreciate help here, especially on the former.

@domenic domenic force-pushed the async-iterable branch 7 times, most recently from d085aa6 to 93860f4 Compare May 18, 2020 22:18
@domenic domenic changed the title WIP: implement async iteratables Implement async iterables May 18, 2020
@domenic
Copy link
Member Author

domenic commented May 18, 2020

OK, this is ready for review. I've integration-tested it with whatwg/streams#1035 so the generated code works. But the source code could still benefit from review, and maybe we could pretty up the generated code. And if anyone has better ideas than [WebIDL2JSHasReturnSteps], please let me know.

I put this on top of #225, but I don't think that's a strict dependency. I also tried to separate this out into semi-logical commits to show my progress and separate the parts I'm sure of from the parts I wasn't, but rebaselining the snapshot file is a pain, so that kind of stopped; it's probably best to just review the whole thing.

README.md Outdated

[Asynchronous iterable declarations](https://heycam.github.io/webidl/#idl-async-iterable) require the implementation class to implement the following symbol-named methods, corresponding to algorithms from the Web IDL specification. The `utils` object below refers to the default export from the generated utilities file `utils.js`.

- `utils.asyncIteratorNext`: corresponds to the [get the next iteration result](https://heycam.github.io/webidl/#dfn-get-the-next-iteration-result) algorithm, and receives a single argument containing an instance of the generated async iterator. For pair asynchronous iterables, the return value must be a `[key, value]` pair array, or `undefined` to signal the end of the iteration. For value asynchronous iterables, the return value must be the value, or `undefined` to signal the end of the iteration.
Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, this seems kind of bad. Streams can contain undefined values. Probably a spec bug?

Copy link
Member

Choose a reason for hiding this comment

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

Web IDL doesn't really have a native “undefined” type though, so when the spec says "It must return a Promise that either rejects, resolves with undefined to signal the end of the iteration" in the IDL section (rather than the ES bindings section) I guess it's a bit unclear what exactly this "undefined" refers to. The conversion algorithms for any says "an object reference to a special object that represents the ECMAScript undefined value".

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'll push a new commit that follows whatwg/webidl#885

@domenic
Copy link
Member Author

domenic commented May 26, 2020

OK, this is ready for review. It continues to test well with the corresponding whatwg/streams PR.

README.md Outdated Show resolved Hide resolved
lib/constructs/async-iterable.js Outdated Show resolved Hide resolved
}
`;

if (this.iterable.hasReturnSteps) {
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, while at the time I thought having an external attribute would be cleaner, I now see that it makes it inconsistent with utils.asyncIteratorInit. I'm open to either this or have return added in the generated file.

Copy link
Member Author

Choose a reason for hiding this comment

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

What do you mean by "have return added in the generated file"?

Copy link
Member

Choose a reason for hiding this comment

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

I meant essentially the other option you provided, which is using utils.asyncIteratorReturn in Impl.prototype to see if we need to add a return method to the prototype. But this looks good too.

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 don't think that works because we only require the Impl class at the bottom of the file, I think for circular dependency reasons. At which time the implementation export might not exist.

lib/constructs/interface.js Outdated Show resolved Hide resolved
lib/constructs/interface.js Outdated Show resolved Hide resolved
lib/constructs/interface.js Outdated Show resolved Hide resolved
}

if (internal.ongoingPromise) {
return Promise.reject(new TypeError("return() cannot be called while an ongoing call to next() has not settled"));
Copy link
Member

Choose a reason for hiding this comment

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

Question: ongoingPromise is only set to null if the promise next() returns gets fulfilled, not when it gets rejected. It seems to me that we might want the [asyncIteratorReturn] method to still be called (which probably cleans up some system resources) even if the last next() failed…?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch. I added a test to the streams WPT to catch this, and will send a Web IDL PR to fix it.

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.

The only thing outstanding for me is whatwg/webidl#891.

}
`;

if (this.iterable.hasReturnSteps) {
Copy link
Member

Choose a reason for hiding this comment

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

I meant essentially the other option you provided, which is using utils.asyncIteratorReturn in Impl.prototype to see if we need to add a return method to the prototype. But this looks good too.

lib/constructs/interface.js Show resolved Hide resolved
@domenic domenic merged commit 8a880db into master Jun 11, 2020
@domenic domenic deleted the async-iterable branch June 11, 2020 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants