I've been playing around with async generators in an attempt to make a "promise ordering" generator which takes an array of promises and yields out promises one by one in the order they resolve or reject. So something like:
async function* orderProms(prom_arr) {
// Make a copy so the splices don't mess it up.
const proms = [...prom_arr];
while (proms.length) {
// Tag each promise with it's index, so that we can remove it for the next loop.
const {prom, index} = await Promise.race(proms.map((prom, index) => prom.then(
() => ({prom, index}),
() => ({prom, index})
)));
proms.splice(index, 1);
yield prom;
}
}
With the idea to consume this generator like so:
const resAfter = (val, delay) => new Promise(res => setTimeout(() => res(val), delay));
const rejAfter = (val, delay) => new Promise((_, rej) => setTimeout(() => rej(val), delay));
const promises = [
resAfter("Third", 3000),
resAfter("First", 1000),
rejAfter("Second", 2000), // NOTE: this one rejects!
];
(async () => {
let ordered = orderProms(promises);
let done = false;
for (let next_promise = ordered.next(); !done; next_promise = ordered.next()) {
const next = await next_promise
.catch(err => ({done: false, value: `Caught error: ${err}`}));
done = next.done;
if (!done) console.log(next.value);
}
})()
However, I've noticed that this will reach up to the second promise, then the generator will halt. It seems to be because of the rejected "second" promise. Calling yield prom
in the generator will create an exception in the generator when the prom
is rejected.
But this is the source of my confusion. I do not want to create an exception here, I just want to yield the rejected promise as the value
of the iterator result. I don't want it to be unwrapped. It's almost like this is being treated as yield await prom;
, but as you can see there is no await
call.
What is going on here and how can I simply yield a rejected promise as-is from this generator.
Here's the above code in a runnable snippet:
async function* orderProms(prom_arr) {
// Make a copy so the splices don't mess it up.
const proms = [...prom_arr];
while (proms.length) {
// Tag each promise with it's index, so that we can remove it for the next loop.
const {prom, index} = await Promise.race(proms.map((prom, index) => prom.then(
() => ({prom, index}),
() => ({prom, index})
)));
proms.splice(index, 1);
yield prom;
}
}
const resAfter = (val, delay) => new Promise(res => setTimeout(() => res(val), delay));
const rejAfter = (val, delay) => new Promise((_, rej) => setTimeout(() => rej(val), delay));
const promises = [
resAfter("Third", 3000),
resAfter("First", 1000),
rejAfter("Second", 2000), // NOTE: this one rejects!
];
(async () => {
let ordered = orderProms(promises);
let done = false;
for (let next_promise = ordered.next(); !done; next_promise = ordered.next()) {
const next = await next_promise
.catch(err => ({done: false, value: `Caught error: ${err}`}));
done = next.done;
if (!done) console.log(next.value);
}
})()