Last active
March 23, 2022 18:18
-
-
Save dfkaye/1260506378b71dac62596acc54174a97 to your computer and use it in GitHub Desktop.
async iterator function that yields resolved promises from an array but in *resolution* order.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 17 Feb 2022 | |
// Not mine!! | |
// It just kicks ass in every way possible! | |
// Slightly modified and commented version of that by @reu (Rodrigo Navarro) | |
// https://gist.github.com/reu/d49c9e45138ed6909db5418f20236376 | |
// 20 Feb 2022: | |
// Change queue semantics (shift, not pop, for FIFO ordering). | |
// Fix missing result problem (should wait before, not after yield). | |
// @reu's version produced in response to | |
// https://twitter.com/yoshuawuyts/status/1494119486429007879 | |
// > Produce a function which takes N Promises, and returns an asyncIterator which yields N resolved values, as soon as they're resolved, not dependent on the order provided. | |
// We'll call this generator function "Q". | |
async function * Q(promises = []) { | |
// Nice lookahead step! | |
// Visit all awaited promises and add a task that removes the current resolved | |
// promise from the awaiting array, and pushes the resolved promise value to | |
// the output queue. | |
var awaiting = promises.slice(); | |
var queue = []; | |
for (var promise of awaiting) { | |
promise.then(result => { | |
awaiting.splice(awaiting.indexOf(promise), 1); | |
queue.push(result); | |
}); | |
} | |
// Run the while loop, yielding any queued items. | |
// Loop exits when all awaited promises are resolved. | |
while (awaiting.length) { | |
// This turned out to be critical! | |
// Await next "tick". This pauses the while() loop, giving the awaited | |
// promises a chance to resolve and call their "then" tasks. | |
await new Promise(res => setTimeout(res, 0)); | |
// 20 Feb 2022 | |
// Surprising result. | |
// Do this *after* waiting for next tick, not before; | |
// Otherwise, not all results are returned! | |
while (queue.length) { | |
// 20 Feb 2022 | |
// Pull from the head, not the tail, of the queue. | |
yield queue.shift(); | |
} | |
} | |
} | |
/* test it out */ | |
// Executor that initializes each promise with a randomized timeout value. | |
function init(i) { | |
return (f,r) => { | |
var t = Math.random(i * 999) * 999; | |
setTimeout( | |
() => f(t), | |
t | |
); | |
} | |
} | |
// Input | |
var promises = Array.from({ length: 10 }, (v,i) => new Promise(init(i))); | |
// Iterator | |
var it = Q(promises); | |
// Output | |
var results = []; | |
// Exec | |
(async () => { | |
for await (var value of it) { | |
results.push(value); | |
} | |
// Render results. | |
console.table(results); | |
// Assert all promises were resolved. | |
console.assert( | |
results.length === promises.length, | |
`Should resolve all promises, but resolved ${results.length} out of ${results.length}.` | |
); | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment