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

Async iterator from iterator of promises via racing #21

Open bakkot opened 2 months ago

bakkot commented 2 months ago

This proposal will include Iterator.prototype.toAsync, which takes a potentially-infinite iterator of values. Calling .next() on the result will call .next() on the underlying thing, await the result and yield it - basically lifting { done: boolean, value: Promise<T> } to Promise<{ done: boolean, value: T }>.

Another way to do it is to take a finite iterator of values, await all of them simultaneously, and yield the results in the order in which they settle. Basically:

async function* raced(promises) {
  while (promises.length > 0) {
    let [p, i] = await Promise.race(promises.map((p, i) => p.then(v => [v, i])));
    promises.splice(i, 1);
    yield p;
  }
}

let sleep = ms => new Promise(res => setTimeout(res, ms));
let promises = [sleep(200).then(x => 2), sleep(100).then(x => 1), sleep(300).then(x => 3)];
for await (let item of raced(promises)) {
  console.log(item);
}
// prints 1, 2, 3

I'm sure this has been proposed before, but I can't find it.

It probably doesn't make sense to put it in this version of the proposal, but since it is similar in signature to toAsync I wanted to mention it anyway.

This is basically sync.toAsync().bufferUnordered(N) with an N set to the length of sync, so it may not make sense to include if we end up adding bufferUnordered.