dash-core
Version:
A foundational toolkit of types, collections, services, and architectural patterns designed to accelerate application development.
106 lines (105 loc) • 4.45 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdaptivePoller = void 0;
const dash_core_1 = require("dash-core");
/**
* Handles polling with adaptive intervals, adjusting delays based on success or failure.
*/
class AdaptivePoller {
currentTimeout;
currentDelay;
lastErrorTime;
polling = false;
operation;
onError;
onFail;
options;
defaultOptions = {
initialDelay: dash_core_1.TimeSpan.fromSeconds(1),
maxDelay: dash_core_1.TimeSpan.fromMinutes(1),
factor: 1.5,
resetPeriod: dash_core_1.TimeSpan.fromMinutes(5),
linearStep: dash_core_1.TimeSpan.fromSeconds(1)
};
/**
* Returns whether the polling operation is currently active.
* @returns {boolean} True if polling is ongoing, false otherwise.
*/
get isPolling() {
return this.polling;
}
/**
* Creates an instance of the AdaptivePoller class.
* @param {pollerOptions} options - Configuration options for the polling mechanism.
* @param {TimeSpan} options.initialDelay - The initial delay before the first polling attempt.
* @param {TimeSpan} options.maxDelay - The maximum delay between polling attempts.
* @param {number} options.factor - The factor by which the delay will be multiplied after each failure.
* @param {TimeSpan} options.resetPeriod - The period after which the delay will reset to the initial value if no errors occur.
* @param {TimeSpan} options.linearStep - The step used to decrease the delay linearly on success.
* @param {ServiceLogger} [options.logger] - Optional logger to log events during polling.
*/
constructor(options) {
this.options = { ...this.defaultOptions, ...options };
this.currentDelay = this.options.initialDelay.totalMilliseconds;
}
/**
* Starts the polling process, which continues until explicitly stopped.
* @param {() => Promise<void>} operation - The operation to perform during each poll cycle.
* @param {(error: Error) => void} onError - Optional callback to handle errors during polling.
* @param {(error: Error) => void} onFail - Optional callback to handle failure events (when the interval exceeds maxDelay).
*/
start(operation, onError, onFail) {
if (this.polling)
return;
this.polling = true;
this.operation = operation;
this.onError = onError;
this.onFail = onFail;
this.currentDelay = this.options.initialDelay.totalMilliseconds;
this.runPoll();
this.options.logger?.info('AdaptivePolling started');
}
/**
* Stops the polling process.
*/
stop() {
this.polling = false;
clearTimeout(this.currentTimeout);
this.options.logger?.info('AdaptivePolling stopped');
}
async runPoll() {
if (!this.polling || !this.operation)
return;
try {
await this.operation();
const now = Date.now();
// Reset the delay if no error occurred within the reset period
if (!this.lastErrorTime || (now - this.lastErrorTime) >= this.options.resetPeriod.totalMilliseconds) {
const initialDelay = this.options.initialDelay.totalMilliseconds;
const linearDifference = this.currentDelay - this.options.linearStep.totalMilliseconds;
this.currentDelay = Math.max(initialDelay, linearDifference);
}
}
catch (error) {
this.lastErrorTime = Date.now();
this.currentDelay = this.currentDelay * this.options.factor;
const resultError = error instanceof Error ? error : new Error(JSON.stringify(error));
if (this.currentDelay >= this.options.maxDelay.totalMilliseconds) {
// If delay exceeds maxDelay, trigger the onFail callback
this.currentDelay = this.options.maxDelay.totalMilliseconds;
this.onFail?.(resultError);
}
else {
// If an error occurred but delay is still within max, trigger the onError callback
this.onError?.(resultError);
}
}
finally {
this.scheduleNextPoll();
}
}
scheduleNextPoll() {
this.currentTimeout = setTimeout(() => this.runPoll(), this.currentDelay);
}
}
exports.AdaptivePoller = AdaptivePoller;