UNPKG

ic-websocket-js

Version:
140 lines 4.43 kB
export class BaseQueue { constructor(args) { this._queue = []; this._canProcess = true; this._isProcessing = false; if (!args.itemCallback) { throw new Error("itemCallback is required"); } this._itemCallback = args.itemCallback; if (args.isDisabled) { this.disable(); } } enable() { this._canProcess = true; } disable() { this._canProcess = false; } add(item) { this._queue.push(item); } addAndProcess(item) { this.add(item); this.process(); } enableAndProcess() { this.enable(); this.process(); } process() { if (!this._canProcess) { return; } if (this._isProcessing) { return; } this._isProcessing = true; this._processNext(); } _processNext() { if (!this._canProcess) { return; } if (this._queue.length === 0) { this._isProcessing = false; return; } const item = this._queue.shift(); // process the item, making sure we wait for the result before processing the next item Promise.resolve(this._itemCallback(item)).then((shouldContinue) => { if (shouldContinue) { this._processNext(); } else { this._isProcessing = false; } }).catch(() => { this._isProcessing = false; }); } } export class AckMessagesQueue { constructor(args) { this._queue = []; this._lastAckTimeout = null; if (!args.expirationMs) { throw new Error("checkTimeoutMs is required"); } this._expirationMs = Math.floor(args.expirationMs); // make sure it's an integer if (!args.timeoutExpiredCallback) { throw new Error("timeoutExpiredCallback is required"); } this._timeoutExpiredCallback = args.timeoutExpiredCallback; } _startLastAckTimeout() { this._lastAckTimeout = setTimeout(() => { this._onTimeoutExpired(this._queue); }, this._expirationMs); } _restartLastAckTimeout() { if (this._lastAckTimeout) { clearTimeout(this._lastAckTimeout); } this._startLastAckTimeout(); } _onTimeoutExpired(items) { this._timeoutExpiredCallback(items.map((item) => item.sequenceNumber)); this._queue = []; } add(sequenceNumber) { const last = this.last(); if (last && sequenceNumber <= last.sequenceNumber) { throw new Error(`Sequence number ${sequenceNumber} is not greater than last: ${last.sequenceNumber}`); } this._queue.push({ sequenceNumber, addedAt: Date.now(), }); if (!this._lastAckTimeout) { this._startLastAckTimeout(); } } ack(sequenceNumber) { const index = this._queue.findIndex((item) => item.sequenceNumber === sequenceNumber); if (index >= 0) { // remove all items up to and including the acked item this._queue.splice(0, index + 1); } else { const last = this.last(); // we throw an error only if the received sequence number is not in the queue // and is greater than the last sequence number in the queue if (last && sequenceNumber > last.sequenceNumber) { throw new Error(`Sequence number ${sequenceNumber} is greater than last: ${last.sequenceNumber}`); } } // for the remaining items in the queue, check if they have expired // if yes, call the callback for the expired items const expiredItems = this._queue.filter((item) => Date.now() - item.addedAt >= this._expirationMs); if (expiredItems.length > 0) { return this._onTimeoutExpired(expiredItems); } this._restartLastAckTimeout(); } last() { if (this._queue.length === 0) { return null; } return this._queue[this._queue.length - 1]; } clear() { this._queue = []; if (this._lastAckTimeout) { clearTimeout(this._lastAckTimeout); this._lastAckTimeout = null; } } } //# sourceMappingURL=queues.js.map