2021-12-11 22:15:23 +00:00
|
|
|
module.exports = class Backoff { // Internal library / utility for a class to retry a callback with delays, etc.
|
2021-12-11 22:10:26 +00:00
|
|
|
constructor(min = 500, max = null) {
|
|
|
|
this._timeoutId = null; // Setup internal vars
|
|
|
|
this.fails = 0;
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
this.min = min; // Setup args
|
|
|
|
this.max = max ?? (min * 10);
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
this.current = min;
|
2021-12-09 16:25:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
get pending() { // If timeout currently set / waiting
|
|
|
|
return this._timeoutId !== null;
|
2021-12-09 16:25:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
succeed() { // Reset state on succeed
|
|
|
|
this.current = this.min;
|
|
|
|
this.fails = 0;
|
2021-12-09 16:25:14 +00:00
|
|
|
|
|
|
|
this.cancel();
|
|
|
|
}
|
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
fail(callback) { // On fail, wait and callback
|
|
|
|
const delay = this.current * 2;
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
this.current = Math.min(this.current + delay, this.max);
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
this.fails += 1; // Bump fails
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
if (!callback) return this.current; // No callback given, skip rest of this
|
|
|
|
if (this._timeoutId !== null) throw new Error('Callback already pending call'); // Timeout already set as waiting for another callback to call, throw error
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
this._timeoutId = setTimeout(() => { // Set new timeout
|
|
|
|
try {
|
|
|
|
callback(); // Run callback
|
|
|
|
} finally {
|
2022-01-21 12:39:00 +00:00
|
|
|
this._timeoutId = null; // Stop tracking timeout internally as it's been executed
|
2021-12-09 16:25:14 +00:00
|
|
|
}
|
2021-12-11 22:10:26 +00:00
|
|
|
}, this.current);
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
return this.current;
|
2021-12-09 16:25:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
cancel() { // Cancel current timeout
|
|
|
|
if (this._timeoutId === null) return; // If no timeout already, do nothing
|
2021-12-09 16:25:14 +00:00
|
|
|
|
2021-12-11 22:10:26 +00:00
|
|
|
clearTimeout(this._timeoutId); // Stop timeout
|
|
|
|
this_timeoutId = null; // Stop tracking timeout internally as it's been executed
|
2021-12-09 16:25:14 +00:00
|
|
|
}
|
2021-12-11 22:10:26 +00:00
|
|
|
};
|