export class Mutex { currentlyRunning: Promise | null = null; waiting: Array<() => Promise> = []; async runExclusive(fn: () => Promise): Promise { const outerPromise = new Promise((resolve, reject) => { const wrappedCallback: () => Promise = () => { return fn() .then((v: T) => resolve(v)) .catch((e: any) => reject(e)); }; this.enqueueCallbackForMutex(wrappedCallback); }); return outerPromise; } private enqueueCallbackForMutex(callback: () => Promise) { if (this.currentlyRunning === null) { this.currentlyRunning = callback().finally(() => { const nextCb = this.waiting.shift(); if (nextCb === undefined) { this.currentlyRunning = null; } else { this.enqueueCallbackForMutex(nextCb); } }); this.waiting.length = 0; } else { this.waiting.push(callback); } } }