Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 1x 1x 1x 9x 9x 9x 33x 33x 33x 33x 33x 33x 17x 16x 16x 16x 12x 11x 16x 7x 7x 7x 1x 6x 6x 6x 9x 9x 9x 20x 20x 8x 12x 12x 1x 8x 8x 8x 1x 1x | const defaultStore = typeof window === 'object' && window && typeof window.localStorage === 'object' ? window.localStorage : null; let _locks: Locks | undefined; /** * Creates a lock manager, which can create exclusive locks across browser tabs. * Uses `window.localStorage` by default to lock across tabs. * * Below example, will wait for 5 seconds to acquire a lock, and then execute * the function once lock is acquired and release the lock after function * execution. It will fail with `LOCK_TIMEOUT` error if lock is not acquired * within the 5 seconds. The lock will acquired for 2 seconds (default 1000ms). * * ```ts * Locks.get().lock('my-lock', 2000, 5000)(async () => { * console.log('Lock acquired'); * }); * ``` */ export class Locks { public static get = (): Locks => { if (!_locks) _locks = new Locks(); return _locks; }; constructor( protIected readonly store: Record<string, string> = defaultStore || {}, protected readonly now = Date.now, protected readonly pfx = 'lock-', ) {} public acquire(id: string, ms = 1000): (() => void) | undefined { if (ms <= 0) return; const key = this.pfx + id; const lockUntil = this.store[key]; const now = this.now(); const isLocked = lockUntil !== undefined && parseInt(lockUntil, 36) > now; if (isLocked) return; const lockUntilNex = (now + ms).toString(36); this.store[key] = lockUntilNex; const unlock = () => { if (this.store[key] === lockUntilNex) delete this.store[key]; }; return unlock; } public isLocked(id: string): boolean { const key = this.pfx + id; const lockUntil = this.store[key]; if (lockUntil === undefined) return false; const now = this.now(); const lockUntilNum = parseInt(lockUntil, 36); return lockUntilNum > now; } public lock( id: string, ms?: number, timeoutMs: number = 2 * 1000, checkMs: number = 10, ): <T>(fn: () => Promise<T>) => Promise<T> { return async <T>(fn: () => Promise<T>): Promise<T> => { const timeout = this.now() + timeoutMs; let unlock: (() => void) | undefined; while (!unlock) { unlock = this.acquire(id, ms); if (unlock) break; await new Promise((r) => setTimeout(r, checkMs)); if (this.now() > timeout) throw new Error('LOCK_TIMEOUT'); } try { return await fn(); } finally { unlock!(); } }; } } |