UNPKG

nav-connect

Version:

Nav Online Invoice connection module

74 lines 2.79 kB
export class RequestQueue { _minIntervalMs; _lastRequestTime = 0; _queue = []; _processing = false; constructor(minIntervalMs) { this._minIntervalMs = minIntervalMs; } markRequestSent() { this._lastRequestTime = Date.now(); } /** * Queues `fn` so it runs at most once per `minIntervalMs` (measured from * the last actually sent request). The returned promise settles with * `fn`'s value or rejection: each queued task wires `resolve`/`reject` * onto `fn()` via `run().then(resolve, reject)`, so the caller always * receives the outcome even though the task itself never rejects. * When the queue is idle, `processQueue()` is started to drain it. */ enqueue(fn) { if (this._minIntervalMs <= 0) { return fn(); } return new Promise((resolve, reject) => { const run = async () => { const now = Date.now(); const wait = this._lastRequestTime + this._minIntervalMs - now; if (wait > 0) { await RequestQueue.sleep(wait); } return fn(); }; this._queue.push(() => run().then(resolve, reject)); if (!this._processing) { this._processing = true; void this.processQueue(); } }); } /** * Drains the queue as long as it is non-empty, then resets `_processing` * so a future `enqueue` restarts the loop. State flow: * - idle (`_processing === false`): the next `enqueue` flips the flag and * starts `processQueue` without awaiting it. * - processing: further `enqueue` calls only append tasks; the running * loop picks them up. * - each task settles its own promise (see `enqueue`), so the try/catch * below is defensive only: a task must never stop the loop or strand * `_processing` as `true`. * The `finally` guarantees `_processing` is cleared even if a task throws. */ async processQueue() { try { while (this._queue.length > 0) { const task = this._queue.shift(); try { await task(); } catch { // Tasks settle their own promise (the caller gets the rejection via // .then(resolve, reject)), so this branch is defensive only: a task // must never stop the queue or strand _processing. } } } finally { this._processing = false; } } static sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } } //# sourceMappingURL=rateLimiter.js.map