ic-websocket-js
Version:
IC WebSocket on the Internet Computer
140 lines • 4.43 kB
JavaScript
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