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(f, n) {
let exhaustedUnderlyingIterator = false;
try {
const parallel = new Set();
const add = () => {
let promise = this.next().then(value => ({
first: true,
promise,
value,
}));
parallel.add(promise);
}
// Start n work
for (let i = 0; i < n; ++i) {
add();
}
// Process the work yielding the first one done in the set of n work
while (parallel.size != 0) {
let { first, promise, value } = await Promise.race(parallel);
parallel.delete(promise);
if (first) {
if (value.done) {
exhaustedUnderlyingIterator = true;
continue;
}
let promise = f(value.value).then(value => ({
first: false,
promise,
value,
}));
parallel.add(promise);
} else {
yield value;
if (!exhaustedUnderlyingIterator) {
// Add 1 more work
add();
}
}
}
} finally {
console.log('map done');
if (!exhaustedUnderlyingIterator) this?.return();
}
}
[].values().__proto__.__proto__.unorderedMap = unorderedMap;
(async function*(){})().__proto__.__proto__.__proto__.unorderedMap = unorderedMap;
async function work(t) {
t.value *= 2;
await new Promise(resolve => setTimeout(resolve, t.delay));
return t;
}
(async () => {
let workDone = 0;
let totalDelay = 0;
const currentTime = performance.now();
for await (const t of makeALotOfWork().unorderedMap(work, 5).unorderedMap(work,...