Promise Pool
Given an array of functions that each return a promise, and a number n, run them all but never more than n at the same time.
As soon as one finishes, the next should start — do not wait for a whole batch to complete. Resolve once every function has finished.
Examples
promisePool([f1, f2, f3], 2) // f1: 300ms, f2: 400ms, f3: 200msresolves after ~500msConstraints
- 1 <= functions.length <= 10
- 1 <= n <= 10
- Every function returns a promise
Notes
- Fixed batches are the common wrong answer: they idle until the slowest member of each batch finishes.
Hints
Promise Pool (reference solution)
One way to do it. The Editorial tab walks through why it is written this way.
Solution
1async function promisePool(functions, n) {
2 let next = 0;
3 const worker = async () => {
4 while (next < functions.length) {
5 const index = next;
6 next += 1;
7 await functions[index]();
8 }
9 };
10 const size = Math.min(n, functions.length);
11 await Promise.all(Array.from({ length: size }, worker));
12}Editorial: Promise Pool
Pooling promises
Promise.all starts everything at once. When "everything" is fifty network requests, that is a stampede — the browser queues them anyway, the server sees a spike, and the slowest response still gates the result. A pool caps how many run concurrently.
Approach
The naive fix is batching: slice the list into groups of n and await each group. It is wrong in a way that is easy to miss — a group cannot start until the slowest member of the previous one finishes, so a fast task sits idle behind a slow one.
The fix is to think in workers rather than batches. Start n of them; each pulls the next unclaimed task, awaits it, and loops. A worker that finishes early immediately takes more work.
Implementation
async function promisePool(functions, n) { let next = 0; const worker = async () => { while (next < functions.length) { const index = next; next += 1; await functions[index](); } }; const size = Math.min(n, functions.length); await Promise.all(Array.from({ length: size }, worker)); }
Worth knowing
A shared next counter is safe without any locking because JavaScript is single-threaded: next += 1 cannot interleave. Math.min(n, functions.length) avoids spawning workers with nothing to do.
Time is the slowest possible packing of the tasks rather than their sum, and memory holds at most n in-flight promises however long the list is.