tc39 / proposal-async-iterator-helpers

Methods for working with async iterators in ECMAScript
https://tc39.es/proposal-async-iterator-helpers/
95 stars 3 forks source link

add out-of-order variants of AsyncIterator.prototype methods #7

Open michaelficarra opened 1 year ago

michaelficarra commented 1 year ago

As discussed in plenary.

We should have variants of each of the iterator helpers that yield their results in an order that depends on the order of resolution of promises from the parameter. Removing this ordering constraint allows for even more concurrency than just pulling from the underlying iterator before earlier promises from it have resolved would allow. The only constraints will be that no promises yielded by these iterators after a promise that either rejects or resolves with done: true will resolve with done: false.

These variants should be available under the same names on a separate prototype object that inherits from AsyncIterator.prototype. We can have a helper on AsyncIterator.prototype for creating iterators that inherit from this new prototype called something like .unordered().

ljharb commented 1 year ago

That plan sounds great, but does it maybe make sense to split the unordered stuff off into a separate proposal? Or do you anticipate the additions and review not slowing down the ordered async helpers?

michaelficarra commented 1 year ago

@bakkot and I considered that. I think we should keep it together for now, with an option to split it out if the design space turns out to be harder (in the same way we did with async iterator helpers, but before stage 3 this time).

DavidANeil commented 1 year ago

Aren't Streams the out-of-order Iterators?

I wonder if all of the out-of-order concerns can just be handled by making a convenient Iterator->ReadableStream helper. And maybe ReadableStream would be well served by some helper functions on the prototype, like

// We want this:
myStream.map((value) => value * 2);
// instead of the super-complex:
myStream.pipeThrough(new TransformStream({start() {}, transform(chunk, controller) {
    controller.enqueue(chunk * 2);
}}));
michaelficarra commented 1 year ago

@DavidANeil Streams aren't part of the language. They're defined in https://streams.spec.whatwg.org/, so available on the web and in runtimes that conform to the WinterCG Minimum Common Web Platform API. Tangentially, having streams as part of the language would be great for things like the base64 proposal. I'm just not sure if it's realistic for us to get there, given they already exist in these adjacent specs.

bakkot commented 1 year ago

Aren't Streams the out-of-order Iterators?

No, ReadableStreams are ordered. And they have a lot of additional machinery on top of basic iteration, to handle backpressure and so on.

Though you may be interested to know that ReadableStreams are async iterable, and you can get an async iterator from one using getIterator - so this proposal will let you do stream.getIterator().{map,filter,etc}. Also just yesterday the streams standard added the ability to get a ReadableStream from an AsyncIterator, though it'll be a while before that is widely usable (and is not directly relevant to your question).

DavidANeil commented 1 year ago

ReadableStreams are ordered

I guess you are correct, once the items are enqueued.

I suppose I always just reached for ReadableStream when I wanted any-order iteration, because it allowed me to control when I called .enqueue, but that was just a matter of convenience, and isn't anything particular about Streams, and if Iterators had the same convenience then they would be just as "correct".