Keyed Mutex for Per-Key Serialization
Serialize asynchronous work per string key while allowing different keys to run concurrently, with idle lock cleanup.
Code
export class KeyedMutex {
private readonly tails = new Map<string, Promise<void>>();
async run<T>(key: string, task: () => Promise<T>): Promise<T> {
if (typeof key !== "string" || key.length === 0) {
throw new TypeError("key must be a non-empty string");
}
if (typeof task !== "function") throw new TypeError("task must be a function");
const previous = this.tails.get(key) ?? Promise.resolve();
let release: () => void = () => {};
const current = new Promise<void>((resolve) => {
release = resolve;
});
const tail = previous.then(() => current);
this.tails.set(key, tail);
await previous;
try {
return await task();
} finally {
release();
if (this.tails.get(key) === tail) this.tails.delete(key);
}
}
isLocked(key: string): boolean {
return this.tails.has(key);
}
get size(): number {
return this.tails.size;
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.