Go home

Mutex Pattern in JavaScript

Prevent concurrent access to shared resources using a promise-based mutex.

The Problem

Two or more async operations race for the same resource — e.g. double-clicking a button triggers two API calls, or concurrent writes corrupt state.

The Mutex

class Mutex {
  #queue: Promise<void>[] = [];

  acquire(): Promise<() => void> {
    let release!: () => void;
    const prev = this.#queue[this.#queue.length - 1] ?? Promise.resolve();
    const next = new Promise<void>((resolve) => (release = resolve));
    this.#queue.push(next);
    return prev.then(() => release);
  }
}

Usage

const mutex = new Mutex();

async function save(data: unknown) {
  const release = await mutex.acquire();
  try {
    await fetch("/api/save", { method: "POST", body: JSON.stringify(data) });
  } finally {
    release();
  }
}

Calls to save now queue up — only one runs at a time.

Why Not a Simple Flag?

let busy = false;

async function save(data: unknown) {
  if (busy) return; // silently drops the call
  busy = true;
  await fetch("/api/save", { method: "POST", body: JSON.stringify(data) });
  busy = false;
}

A flag either drops calls or lets them through. A mutex queues them — no call is lost, no call overlaps.

Cheatsheet

FlagMutex
Drops callsyesno
Queues callsnoyes
Guarantees ordernoyes