node-ejs-renderer/node_modules/thingies/es2020/Locks.js
2024-06-09 13:55:01 -04:00

80 lines
2.5 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Locks = void 0;
const defaultStore = typeof window === 'object' && window && typeof window.localStorage === 'object' ? window.localStorage : null;
let _locks;
/**
* 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');
* });
* ```
*/
class Locks {
constructor(store = defaultStore || {}, now = Date.now, pfx = 'lock-') {
this.store = store;
this.now = now;
this.pfx = pfx;
}
acquire(id, ms = 1000) {
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;
}
isLocked(id) {
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;
}
lock(id, ms, timeoutMs = 2 * 1000, checkMs = 10) {
return async (fn) => {
const timeout = this.now() + timeoutMs;
let unlock;
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();
}
};
}
}
exports.Locks = Locks;
Locks.get = () => {
if (!_locks)
_locks = new Locks();
return _locks;
};