So... After, literally, days of trying to solve this.
This works (just copy paste into latest chrome with experimental flag turned on)
function timer(time = 500) {
return new Promise(resolve => setTimeout(() => resolve(), time));
}
async function* gen1() {
await timer(100);
yield 1;
await timer(300);
yield 4;
}
async function* gen2() {
await timer(200);
yield 2;
await timer(100);
yield 3;
}
function race(promises) {
return new Promise(resolve =>
promises.forEach((p, index) => {
p.then(value => {
resolve({index, value});
});
})
);
}
async function* mergen(...gens) {
let promises = gens.map((gen, index) =>
gen.next().then(p => ({...p, gen}))
);
while (promises.length > 0) {
yield race(promises)
.then(({index, value: {value, done, gen}}) => {
promises.splice(index, 1);
if (!done)
promises.push(
gen.next().then(({value: newVal, done: newDone}) => ({
value: newVal,
done: newDone,
gen
}))
);
return value;
});
}
}
async function printGen(gen) {
let max = 10;
for await (x of gen) {
if (x) console.log('Next up:', x);
if (--max <= 0) break;
}
}
printGen(mergen(gen1(), gen2())); // 1, 2, 3, 4
It's pre-refactoring, so bare in mind it's not nice and clean yet.
The interesting code is called mergen()
(get it? merge-gen?)
It uses a modified version of Promise.race()
included here as well.
The reason for the use of a modified race
is because I needed to have the index of the promise that finished first.
UPDATE: And now it's an npm module https://github.com/hesher/mergen