A semaphore is concurrency construct that is used to limit or throttle access to a resource. This article shows a simple semaphore class in Node.js that can be used to limit execution of functions.

Usage of the class looks like:

// create a semaphore
const sem = new Semaphore(3);

// create a function that is done after 5s
const run = done => {
   console.log('starting job');

   // call done to release the semaphore
   setTimeout(done, 5000);
};

// run this 5 times
sem.take(run);
sem.take(run);
sem.take(run);
sem.take(run);
sem.take(run);

The outpout will immediately start the first three calls to take. After the first job has finished, the 4th will start. After the second job has finished, the 5ht will start.

Under the covers, our semaphore simply uses a counter to track the number of concurrently executing operations.

class Semaphore {
  constructor(max = 1) {
    this.max = max;
    this._fns = [];
    this._active = 0;
  }

  get remaining() {
    return this._fns.length;
  }

  get active() {
    return this._active;
  }

  take(fn) {
    this._fns.push(fn);
    this._try();
  }

  _done() {
    this._active -= 1;
    this._try();
  }

  _try() {
    if (this._active === this.max || this._fns.length === 0) return;
    let fn = this._fns.shift();
    this._active += 1;
    if (fn) fn(this._done.bind(this));
  }
}

This syntax has similarity to the semaphore package on npm. It's so simply, why bother installing another external dependency!