Controlling Async Concurrency

Starting thousands of asynchronous operations simultaneously can overwhelm APIs, memory, sockets, or downstream systems. Concurrency limiting restricts how many operations are active at once.

Promise.all is useful for independent work but should not automatically be used for unbounded collections. Production systems often need batching, queues, backpressure, retries, timeouts, and cancellation.

Simple Batch Processing
async function processInBatches(items, size, worker) {
  const results = [];

  for (let i = 0; i < items.length; i += size) {
    const batch = items.slice(i, i + size);
    results.push(...await Promise.all(batch.map(worker)));
  }

  return results;
}

const results = await processInBatches(items, 10, processItem);
💡 Only one batch of at most ten operations is active at a time, reducing pressure on external resources.
Key Takeaways
  • Unlimited Promise concurrency can overload systems.
  • Batching is a simple concurrency-control strategy.
  • Production async systems often require backpressure and cancellation.
Knowledge Check

Why can blindly using Promise.all over thousands of items be problematic?