Course contentsShow
← Node.js
Lesson 191 of 1,9803. Asynchronous Patterns and Flow ControlFree lesson

Throttling Concurrent Promise Requests

You will create a limiter function to govern the number of simultaneously executing asynchronous tasks.

In this lesson, you’ll learn how to limit the number of asynchronous tasks running at once to prevent overwhelming your system resources.

The Problem with Unlimited Parallelism

In previous lessons, you learned how to use Promise.all to run tasks in parallel. While fast, firing 10,000 network requests at once can crash your application or get your IP banned by a server. "Throttling" allows you to maintain a "concurrency limit"—ensuring that, for example, only 3 tasks run at any given moment, while the rest wait in a queue.

Implementing a Simple Limiter

To achieve this, we create a function that acts as a gatekeeper. We maintain a pool of "active" tasks and a queue of "pending" tasks. When a task finishes, we automatically pull the next one from the queue.

async function throttle(tasks, limit) {
  const results = [];
  const executing = new Set();

  for (const task of tasks) {
    const p = task().then(res => {
      executing.delete(p);
      return res;
    });
    
    results.push(p);
    executing.add(p);

    // If we hit our limit, wait for the first one to finish
    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }
  return Promise.all(results);
}

How it works

  1. The Set: We use a Set called executing to keep track of promises currently in flight.
  2. The Race: The await Promise.race(executing) line is the secret sauce. By using the Promise.race pattern we learned previously, we pause the loop specifically until the fastest current task completes, freeing up a "slot" in our concurrency limit.
  3. The Loop: The loop continues, adding a new task only when one has finished, ensuring the number of active promises never exceeds your defined limit.

By controlling the flow this way, you make your application resilient against memory spikes and network congestion, turning chaotic bursts of activity into a steady, manageable stream of work.

Key Takeaway: Throttling ensures system stability by using a task queue and Promise.race to enforce a strict concurrency limit on your asynchronous operations.