Limit concurrency
Implement mapLimit(items, limit, worker). worker(item, index) returns a promise. Run at most limit workers at the same time, start the next one as soon as one finishes, and resolve with the results in input order. Reject as soon as a worker rejects. An empty list resolves with []. In the tests a task [30, a] resolves with a after 30 ms and [30, boom, true] rejects with Error(boom); an outcome is [status, value, time]. The answer also shows the peak number of workers running at once, and in some tests when each worker started.
Start from
function mapLimit(items, limit, worker) {
// Run worker over items, at most limit at a time.
}Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1
Keep three counters: the next index to start, how many are running, how many are done.
Hint 2
Write one function that starts workers while running < limit and items remain. Call it at the start and again each time a worker finishes.
Hint 3
Store each result at its index. Resolve when done equals the number of items.
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
JavaScript
function mapLimit(items, limit, worker) {
return new Promise((resolve, reject) => {
const results = new Array(items.length);
let next = 0;
let running = 0;
let done = 0;
let failed = false;
if (items.length === 0) return resolve(results);
function start() {
while (running < limit && next < items.length) {
const index = next++;
running++;
Promise.resolve(worker(items[index], index)).then(
(value) => {
results[index] = value;
running--;
done++;
if (done === items.length) resolve(results);
else if (!failed) start();
},
(error) => {
failed = true;
reject(error);
},
);
}
}
start();
});
}What an interviewer looks for
Uploading fifty files or calling an API with a rate limit needs this. It is a senior favourite because it mixes ordering, a pool of workers and failure handling.