Node.js
Concurrency limit
Learn to limit parallel work with a concurrency limit.
Concurrency limit
Concurrency limit is a pattern used to cap the number of async jobs running at the same time. You build a pool: N workers pull jobs from a queue. Example: mapLimit(items, 2, fn) runs at most 2 jobs at once.
The pattern is: an i counter, a results array and N worker functions. Each worker takes the next job with while (i < items.length), processes it with await fn(items[idx]) and writes the result to results[idx]. All workers are awaited with Promise.all(workers). This is used to avoid sending hundreds of API requests at once; the server does not reject. The limit parameter sets the worker count; a typical value is 2–10.
The worker pattern
A worker is an async function that pulls jobs from the queue with a while loop. Example: async function worker() { while (i < items.length) { const idx = i; i++; results[idx] = await fn(items[idx]); } }.
The const idx = i; i++; pattern is critical: it copies i into a local idx first, then increments. Two workers never take the same idx. The results[idx] assignment puts the result in the right slot; even if running order differs, results follow the original order. N workers start: for (let w = 0; w < limit; w++) workers.push(worker()). Then await Promise.all(workers) waits for all. This is the standard way to keep parallelism bounded.
Exercise Write the mapLimit function
Write async function mapLimit(items, limit, fn): const results = []; let i = 0;. Add async function worker() with while (i < items.length) { const idx = i; i++; results[idx] = await fn(items[idx]); }. Start N workers with for (let w = 0; w < limit; w++) workers.push(worker()). await Promise.all(workers); return results;. main() is ready: console.log("Results: " + results.join(",")).
Show example solution
async function mapLimit(items, limit, fn) {
const results = [];
let i = 0;
async function worker() {
while (i < items.length) {
const idx = i;
i++;
results[idx] = await fn(items[idx]);
}
}
const workers = [];
for (let w = 0; w < limit; w++) {
workers.push(worker());
}
await Promise.all(workers);
return results;
}
async function main() {
const items = [1, 2, 3];
const fn = async function(x) { return x * 10; };
const results = await mapLimit(items, 2, fn);
console.log("Results: " + results.join(","));
}
main();
Test yourself
-
Match each piece to its role
- limit
- Worker count
- worker
- Pulls a job from the queue
- Promise.all
- Awaits all workers
- results[idx]
- Writes result by index
-
Complete the worker while condition
while (i < items.____) { const idx = i; i++; results[idx] = await fn(items[idx]); }
Show answer
while (i < items.length) { const idx = i; i++; results[idx] = await fn(items[idx]); }
-
Why use mapLimit?
const results = await mapLimit(items, 2, fn)
- To limit how many tasks run at the same time
- To catch errors
- To filter the array
- To sort the array
Show answer
To limit how many tasks run at the same time — mapLimit caps the number of jobs running at once.
Next lesson: Retry pattern