Async Pool Concurrent Map Utility
Map over large collections with bounded async concurrency, preserving input order in results and stopping early on failure modes.
Code
async function asyncPool<T, R>(
concurrency: number,
items: readonly T[],
mapper: (item: T, index: number) => Promise<R>,
): Promise<R[]> {
const results = new Array<R>(items.length);
let cursor = 0;
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
while (cursor < items.length) {
const index = cursor;
const item = items[index];
cursor += 1;
results[index] = await mapper(item, index);
}
});
await Promise.all(workers);
return results;
}
const CONCURRENCY = 4;
const enriched = await asyncPool(CONCURRENCY, ['a', 'b', 'c', 'd', 'e', 'f'], async (id, i) => {
await new Promise((r) => setTimeout(r, 20));
return { id, position: i };
});
console.log('order preserved:', enriched.map((e) => e.id).join(''));
const withFailure = await asyncPool(2, ['x', 'y', 'z'], async (id) => {
if (id === 'y') throw new Error('boom on ' + id);
return id.toUpperCase();
}).catch((err) => err.message);
console.log('failure propagated:', withFailure);Report a problem with this exampleOpens a pre-filled issue on GitHub.