JSFiddle - React, Tailwind, and code Playground
JavaScript
let makeALotOfWorkDelay = 0;
async function* makeALotOfWork() {
let value = 0;
while (true) {
// In a real async generator this could be doing some work also before emitting items. This simulates that behavior
const delay = Math.floor(Math.random() * 100);
makeALotOfWorkDelay += delay;
await new Promise(resolve => setTimeout(resolve, delay));
yield ({ delay: Math.floor(Math.random() * 1000) + 10, value: value++ });
}
}
async function* unorderedMap(fn, n) {
try {
// Start n work
const parallel = new Set();
function addTask(iterator) {
const obj = {};
const promise = new Promise(async resolve => {
resolve({ obj, result: await fn(iterator.value) });
});
obj.promise = promise;
parallel.add(promise);
}
for (let i = 0; i < n; ++i) {
let iteratorPromise = this.next();
let itrOrTask = await Promise.race([iteratorPromise, ...parallel]);
while (parallel.has(itrOrTask?.obj?.promise)) {
parallel.delete(itrOrTask.obj.promise);
yield itrOrTask.result; // a task
itrOrTask = await Promise.race([iteratorPromise, ...parallel]);
}
const iterator = itrOrTask;
if (iterator.done) return;
addTask(iterator);
}
// Process the work yielding the first one done in the set of n work
while (parallel.size != 0) {
const task = await Promise.race([...parallel]);
parallel.delete(task.obj.promise);
yield task.result;
// Add 1 more work
let iteratorPromise = this.next();
let itrOrTask = await Promise.race([iteratorPromise, ...parallel]);
while (parallel.has(itrOrTask?.obj?.promise)) {
parallel.delete(itrOrTask.obj.promise);
yield itrOrTask.result; // a task
itrOrTask = await Promise.race([iteratorPromise, ...parallel]);
}
const iterator = itrOrTask;
if (iterator.done) return;
addTask(iterator);
}
} finally {
console.log('map done');
...