Skip to content

Instantly share code, notes, and snippets.

@tomtheisen
Last active August 22, 2025 20:22
Show Gist options
  • Select an option

  • Save tomtheisen/42bfdd1bc0011964acb4ecec54fc77c6 to your computer and use it in GitHub Desktop.

Select an option

Save tomtheisen/42bfdd1bc0011964acb4ecec54fc77c6 to your computer and use it in GitHub Desktop.
Promise Queue
function promiseAndCallbacks() {
let resolve: () => void;
let reject: (reason?: any) => void;
const promise = new Promise<void>((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
export type MutexLockHandle = {
release: () => void;
}
/**
* A simple mutex for async code.
* Usage:
* const mutex = new Mutex();
*
* async function criticalSection() {
* const lock = await mutex.acquireLock();
* try {
* // critical section code, must be async
* }
* finally { lock.release(); }
* }
*/
export class Mutex {
// Infinitely chained promise queue
// This is not a memory leak, as resolved promises are GC'd
// Verified using browser heap snapshots with millions of locks acquired/released
#queue: Promise<void> = Promise.resolve();
async acquireLock(): Promise<MutexLockHandle> {
const pac = promiseAndCallbacks();
const originalQueue = this.#queue;
this.#queue = this.#queue.then(() => pac.promise);
await originalQueue;
return { release: pac.resolve };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment