typescriptintermediate

Promise Pool for Batch Processing

Process large arrays with controlled concurrency using a promise pool pattern with progress tracking.

typescript
interface PoolOptions<T, R> {
  items: T[];
  concurrency: number;
  handler: (item: T, index: number) => Promise<R>;
  onProgress?: (completed: number, total: number) => void;
}

async function promisePool<T, R>(options: PoolOptions<T, R>): Promise<R[]> {
  const { items, concurrency, handler, onProgress } = options;
  const results: R[] = new Array(items.length);
  let nextIndex = 0;
  let completed = 0;

  async function worker() {
    while (nextIndex < items.length) {
      const i = nextIndex++;
      results[i] = await handler(items[i], i);
      completed++;
      onProgress?.(completed, items.length);
    }
  }

  const workers = Array.from(
    { length: Math.min(concurrency, items.length) },
    () => worker(),
  );
  await Promise.all(workers);
  return results;
}

// Batch with chunking
async function processBatches<T, R>(
  items: T[],
  batchSize: number,
  handler: (batch: T[], batchIndex: number) => Promise<R[]>,
): Promise<R[]> {
  const results: R[] = [];
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    const batchResults = await handler(batch, Math.floor(i / batchSize));
    results.push(...batchResults);
    console.log(`Batch ${Math.floor(i / batchSize) + 1}: ${results.length}/${items.length}`);
  }
  return results;
}

// Usage
async function main() {
  const urls = Array.from({ length: 50 }, (_, i) => `https://api.example.com/${i}`);

  console.time('pool');
  const results = await promisePool({
    items: urls,
    concurrency: 10,
    handler: async (url, i) => {
      await new Promise(r => setTimeout(r, Math.random() * 200));
      return { url, ok: true };
    },
    onProgress: (done, total) => {
      if (done % 10 === 0) console.log(`Progress: ${done}/${total}`);
    },
  });
  console.timeEnd('pool');
  console.log(`Processed ${results.length} items`);
}

main();

Use Cases

  • Bulk API requests
  • Image processing pipelines
  • Data migration scripts

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.