A promise pool runs many asynchronous tasks but caps how many are in flight at once. Promise.all fires everything simultaneously — fine for three requests, a problem for three thousand, where you'd exhaust connections or hit rate limits. A pool keeps a fixed number running and starts the next task only when a slot frees up.
Implement promisePool(taskFns, concurrency). taskFns is an array of functions, each returning a promise. Run at most concurrency at a time, and resolve to an array of results in the same order as taskFns — regardless of the order they finish.
function promisePool(taskFns, concurrency) {
// taskFns: (() => Promise<T>)[]
// returns Promise<T[]> — results in taskFns order
}
const tasks = [
() => fetch('/a'), () => fetch('/b'),
() => fetch('/c'), () => fetch('/d'),
];
await promisePool(tasks, 2); // at most 2 requests in flight; results in [a,b,c,d] order
// Order is preserved even though task 2 finishes first:
await promisePool([() => delay(30, 'a'), () => delay(5, 'b')], 2); // ['a', 'b']
taskFns holds functions. A task doesn't start until you call its function, which is how you control when it begins.concurrency tasks running; start the next only when one settles.taskFns order, not completion order (like Promise.all).taskFns resolves to []; concurrency larger than the task count just runs them all.