Mutex in JavaScript/TypeScript

Examples of mutexes.

Published: 6/21/2024

Initialise a mutex as needed on your class; use;

// FILE: mutex.ts

class Mutex {
  private _locking: Promise<void>;
  private _locked: boolean;

  constructor() {
    this._locking = Promise.resolve();
    this._locked = false;
  }

  isLocked(): boolean {
    return this._locked;
  }

  lock(): Promise<() => void> {
    this._locked = true;
    let unlockNext: () => void;
    let willLock = new Promise<void>((resolve) => (unlockNext = resolve));
    willLock.then(() => (this._locked = false));
    let willUnlock = this._locking.then(() => unlockNext);
    this._locking = this._locking.then(() => willLock);
    return willUnlock;
  }
}

export default Mutex;

Usage:

nextIdMutex = new Mutex();

// read and decrement id
public async takeNextId() {
  const unlock = await this.nextIdMutex.lock();

  let id: number = 0;

  try {
    await new Promise<void>((resolve) => {
      setTimeout(() => {
        console.log('waiting');
        resolve();
      }, 10000);
    });

    id = await this.storage?.get('nextId');
    await this.storage?.set('nextId', id - 1);
  } catch (err) {
    console.log(err);
  }
  unlock();

  return id;
}
  • Create the mutex
  • For critical section
    • await lock and save to unlock
    • do critical work
    • unlock()
  • I added a blocking promise inside to help prove it works
class Mutex {
  constructor() {
    this._locking = Promise.resolve();
    this._locked = false;
  }

  isLocked() {
    return this._locked;
  }

  lock() {
    this._locked = true;
    let unlockNext;
    let willLock = new Promise(resolve => unlockNext = resolve);
    willLock.then(() => this._locked = false);
    let willUnlock = this._locking.then(() => unlockNext);
    this._locking = this._locking.then(() => willLock);
    return willUnlock;
  }
}

export default Mutex;