Locks.js 2.6 KB

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