In some cases, we may need to control the number of concurrent requests.

For example, when we are writing download or crawling tools, some websites may have a limit on the number of concurrent requests.

In browsers, the maximum number of TCP connections from the same origin is limited to 6. This means that if you use HTTP1.1 when sending more than 6 requests at the same time, the 7th request will wait until the previous one is processed before it starts.

We can use the following simple example to test it. First the client code:

```javascript
void (async () => {
  await Promise.all(
    [...new Array(12)].map((_, i) =>
      fetch(`http://127.0.0.1:3001/get/${i + 1}`),
    ),
  );
})();
```

Next is the brief code for the service:

```javascript
router.get('/get/:id', async (ctx) => {
  const order = Number(ctx.params.id);
  if (order % 2 === 0 && order <= 6) {
    await sleep(1000);
  }
  await sleep(1000);
  ctx.body = 1;
});
```

In the first 6 requests, if the order is even, then wait for **2s**, if not, wait for **1s**. Then open the Network in DevTools and you can get the following picture:

![JavaScript Promise concurrency control example showing Network DevTools waterfall diagram](https://img.upweb.dev/content/3d5c671b-1117x593.png)

Looking at the Time and Waterfall columns show that it is a model of the request concurrency limit. So I want to achieve a similar function, how to do it?
I open-sourced [p-limiter](https://github.com/islizeqiang/p-limiter) on Github. Here’s the same Gist:

```typescript
class Queue<T> {
  #tasks: T[] = [];

  enqueue = (task: T) => {
    this.#tasks.push(task);
  };

  dequeue = () => this.#tasks.shift();

  clear = () => {
    this.#tasks = [];
  };

  get size() {
    return this.#tasks.length;
  }
}

class PromiseLimiter {
  #queue = new Queue<() => Promise<void>>();
  #runningCount = 0;
  #limitCount: number;

  constructor(limitCount: number) {
    this.#limitCount = limitCount;
  }

  #next = () => {
    if (this.#runningCount < this.#limitCount && this.#queue.size > 0) {
      this.#queue.dequeue()?.();
    }
  };

  #run = async <R = any>(
    fn: () => Promise<R>,
    resolve: (value: PromiseLike<R>) => void,
  ) => {
    this.#runningCount += 1;
    const result = (async () => fn())();
    resolve(result);

    try {
      await result;
    } catch {
      // ignore
    }

    this.#runningCount -= 1;
    this.#next();
  };

  get activeCount() {
    return this.#runningCount;
  }

  get pendingCount() {
    return this.#queue.size;
  }

  limit = <R = any>(fn: () => Promise<R>) =>
    new Promise<R>((resolve) => {
      this.#queue.enqueue(() => this.#run(fn, resolve));
      this.#next();
    });
}

export default PromiseLimiter;
```

This can be achieved in less than 70 lines of code above, you can try it out in [StackBlitz](https://stackblitz.com/edit/js-fyzurv?file=index.js).

You can see that it works as expected. Without resorting to any third-party libraries, the simple code model is just that.

So do you have any other use cases?

_If you found this helpful, consider [subscribing to my newsletter](https://upweb.dev) for weekly web development insights and updates. Thanks for reading!_
