TypeScript: running async tasks with a concurrency limit
Starting thousands of asynchronous jobs at once can overwhelm a server, a database, or the network. A pool limits how many run at the same time. The pool has N workers, and each worker repeatedly takes the next unstarted task, awaits it, and stores its result at the task's own index.
TypeScript
async function tick(times: number): Promise<void> {
for (let i = 0; i < times; i++) {
await null;
}
}
async function pool<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]> {
const results: T[] = new Array(tasks.length);
let next = 0;
async function worker(): Promise<void> {
while (next < tasks.length) {
const index = next++;
results[index] = await tasks[index]();
}
}
await Promise.all(Array.from({ length: limit }, () => worker()));
return results;
}
let running = 0;
let peak = 0;
const finished: string[] = [];
function task(name: string, ticks: number): () => Promise<string> {
return async () => {
running++;
peak = Math.max(peak, running);
await tick(ticks);
running--;
finished.push(name);
return name.toUpperCase();
};
}
const tasks = [task("a", 10), task("b", 1), task("c", 2), task("d", 1)];
pool(tasks, 2).then((results) => {
console.log("results in input order: " + results.join(","));
console.log("finished order: " + finished.join(","));
console.log("peak concurrency: " + peak);
});