@poomcha/retrier
Version:
Retry synchronous or asynchronous functions.
585 lines (582 loc) • 18.9 kB
JavaScript
// src/helpers/Utils.ts
var UtilsNumber = class {
/**
* Returns true if n is a safe integer, between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER.
* @param n any
* @returns boolean
*/
static isInteger(n) {
return Number.isSafeInteger(n);
}
/**
* Returns true if n is positive.
* @param n number
* @returns boolean
*/
static isPositive(n) {
return n >= 0;
}
/**
* Returns true if n is an integer. Else raise NonIntegerError.
* @param n number
* @returns boolean
* @throws NonIntegerError
*/
static checkIntegerNumber(n) {
if (this.isInteger(n)) return n;
throw new Error("NonSafeIntegerError");
}
/**
* Returns true if n is positive. Else raise NonPositiveError.
* @param n number
* @returns boolean
* @throws NonPositiveError
*/
static checkPositiveNumber(n) {
if (this.isPositive(n)) return n;
throw new Error("NonPositiveError");
}
/**
* Returns true if n is a positive integer
* @param n any
* @returns boolean
* @throws
*/
static checkIntegerPositive(n) {
try {
return this.checkPositiveNumber(this.checkIntegerNumber(n));
} catch (error) {
throw error;
}
}
};
var UtilsCallback = class {
/**
* Call a callback function with or without its arguments synchronously and return its result or throw error.
*
* @param callback
* @param args Optionnal
*/
static callSyncCallback(callback, args) {
try {
if (args) {
return callback(...args);
}
return callback();
} catch (error) {
throw error;
}
}
/**
* Call a callback function with or without its arguments asynchronously and return its result or throw error.
*
* @param callback
* @param args Optionnal
*/
static async callAsyncCallback(callback, args) {
try {
if (args) {
return await callback(...args);
}
return await callback();
} catch (error) {
throw error;
}
}
};
// src/retrier/Retrier.ts
var Retrier = class _Retrier {
//#region Members
_maxRetries = 0;
_maxRetriesDefault = 2;
_delay = 0;
_delayDefault = 0;
_onSuccess = void 0;
_onFailure = void 0;
//#endregion
//#region Constructor
/**
* Constructs a new Retrier instance with optional configuration options.
* @constructor
*
* @param options - Optional configuration options.
* @param options.maxRetries - The maximum number of retries.
* @param options.delay - Delay between two retries in ms.
* @param options.onSuccess - onSuccess callback, can override default return.
* @param options.onFailure - onFailure callback, can override default error throwing.
* @throws Will throw an error if the maxRetries or delay are not valid positive integer
* [0, Number.MAX_SAFE_INTEGER].
*/
constructor(options) {
if (options) {
const { maxRetries, delay, onSuccess, onFailure } = options;
try {
if (maxRetries) this.setMaxRetries(maxRetries);
else this.setMaxRetries(this._maxRetriesDefault);
if (delay) this.setDelay(delay);
else this.setDelay(this._delayDefault);
if (onSuccess) this.setOnSuccess(onSuccess);
if (onFailure) this.setOnFailure(onFailure);
} catch (error) {
throw error;
}
} else {
try {
this.setMaxRetries(this._maxRetriesDefault);
this.setDelay(this._delayDefault);
} catch (error) {
throw error;
}
}
}
//#endregion
//#region Getters
/**
* Gets the maximum number of retries.
*
* @returns The maximum number of retries.
*/
getMaxRetries() {
return this._maxRetries;
}
/**
* Gets the delay between retries.
*
* @returns Delay between retries.
*/
getDelay() {
return this._delay;
}
/**
* Get the _onSuccess private member.
*
* @returns _onSuccess member
*/
getOnSuccess() {
return this._onSuccess;
}
/**
* Get the _onFailure private member.
*
* @returns _onFailure member.
*/
getOnFailure() {
return this._onFailure;
}
//#endregion
//#region Setters
/**
* Sets the maximum number of retries.
*
* @param n - The number of retries to set.
* @throws Will throw an error if the number is not a valid positive integer.
*/
setMaxRetries(n) {
try {
this._maxRetries = UtilsNumber.checkIntegerNumber(n);
} catch (error) {
throw error;
}
}
/**
* Sets the delay between retries.
*
* @param delay - Delay between retries.
* @throws Will throw an error if the number is not a valid positive integer
*/
setDelay(delay) {
try {
this._delay = UtilsNumber.checkIntegerNumber(delay);
} catch (error) {
throw error;
}
}
/**
* Sets the _onSuccess member
*
* @param onSuccess
* @param onSuccess.callback Callback to call on success
* @param onSuccess.args Arguments to pass to the callback
* @param onSuccess.override If true, override default behaviour of retry functions
*/
setOnSuccess(onSuccess) {
this._onSuccess = onSuccess;
return;
}
/**
* Sets the _onFailure member
*
* @param onFailure
* @param onFailure.callback Callback to call on failure
* @param onFailure.args Arguments to pass to the callback
* @param onFailure.override If true, override default behaviour of retry functions
*/
setOnFailure(onFailure) {
this._onFailure = onFailure;
return;
}
//#endregion
//#region Static Methods
/**
* Retries a synchronous operation, with a specified number of retries.
* Returns the result of the operation as soon as it succeed, or throws
* operation's error.
* Give access to onSuccess and onFailure optionnal handlers to handle
* success or failure.
* onSuccess and onFailure are given operation result as first argument.
*
* @see [source](https://github.com/Poomcha/retrier)
*
*
* @param maxRetries - Positive integer, max number of retries <code>(Number[0, MAX_SAFE_INTEGER])</code>.
* @param callback - Function to retry.
* @param args - Optionnal arguments for callback.
*
*
* @param options - Optionnal options.
*
*
* @param options.onSuccess -
* @param options.onSuccess.callback - Function to run on success.
* @param options.onSuccess.args - Optionnal arguments for onSuccess.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onSuccess.override - Optionnal boolean, if true retrySync will return options.onSuccess.callback result.
*
* @param options.Failure -
* @param options.onFailure.callback - Function to run on failure.
* @param options.onFailure.args - Optionnal arguments for onFailure.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onFailure.override - Boolean, if true retrySync will return options.onFailure.callback result.
*
*
* @returns callback result or options.onSuccess.callback result or options.onFailure.callback result.
*
*
* @throw callback error or options.onSuccess.callback error or options.onFailure.callback error.
*/
static retryStaticSync(maxRetries, callback, args, options) {
try {
return _Retrier._retrySync(callback, maxRetries, args, options);
} catch (error) {
throw error;
}
}
/**
* Retries an asynchronous operation, with a specified number of retries.
* Returns the result of the operation as soon as it succeed, or throws
* operation's error.
* Give access to onSuccess and onFailure optionnal handlers to handle
* success or failure.
* onSuccess and onFailure are given operation result as first argument.
*
* @see [source](https://github.com/Poomcha/retrier)
*
* @param maxRetries - Positive integer, max number of retries <code>(Number[0, MAX_SAFE_INTEGER])</code>.
* @param callback - Function to retry.
* @param args - Optionnal arguments for callback.
*
*
* @param options - Optionnal options.
*
* @param options.delay - Positive integer, delay between 2 retry.
*
* @param options.onSuccess -
* @param options.onSuccess.callback - Function to run on success.
* @param options.onSuccess.args - Optionnal arguments for onSuccess.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onSuccess.override - Optionnal boolean, if true retrySync will return options.onSuccess.callback result.
*
* @param options.Failure -
* @param options.onFailure.callback - Function to run on failure.
* @param options.onFailure.args - Optionnal arguments for onFailure.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onFailure.override - Boolean, if true retrySync will return options.onFailure.callback result.
*
*
* @returns callback result or options.onSuccess.callback result or options.onFailure.callback result.
*
*
* @throw callback error or options.onSuccess.callback error or options.onFailure.callback error.
*/
static async retryStaticAsync(maxRetries, callback, args, options) {
try {
return await _Retrier._retryAsync(callback, maxRetries, args, options);
} catch (error) {
throw error;
}
}
//#endregion
//#region Public Methods
/**
* Retries a synchronous operation, with a specified number of retries.
* Returns the result of the operation as soon as it succeed, or throws
* operation's error.
* Give access to onSuccess and onFailure optionnal handlers to handle
* success or failure.
* onSuccess and onFailure are given operation result as first argument.
*
* @see [source](https://github.com/Poomcha/retrier)
*
*
* @param callback - Function to retry.
* @param args - Optionnal arguments for callback.
*
*
* @param options - Optionnal options, overule instance configuration.
*
* @param options.maxRetries - Positive integer, max number of retries <code>(Number[0, MAX_SAFE_INTEGER])</code>.
*
* @param options.onSuccess -
* @param options.onSuccess.callback - Function to run on success.
* @param options.onSuccess.args - Optionnal arguments for onSuccess.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onSuccess.override - Optionnal boolean, if true retrySync will return options.onSuccess.callback result.
*
* @param options.Failure -
* @param options.onFailure.callback - Function to run on failure.
* @param options.onFailure.args - Optionnal arguments for onFailure.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onFailure.override - Boolean, if true retrySync will return options.onFailure.callback result.
*
*
* @returns callback result or options.onSuccess.callback result or options.onFailure.callback result.
*
*
* @throw callback error or options.onSuccess.callback error or options.onFailure.callback error.
*/
retrySync(callback, args, options) {
try {
const computedOptions = this.makeOptionsSync(
options
);
return _Retrier._retrySync(
callback,
computedOptions.maxRetries,
args,
computedOptions
);
} catch (error) {
throw error;
}
}
/**
* Retries an asynchronous operation, with a specified number of retries.
* Returns the result of the operation as soon as it succeed, or throws
* operation's error.
* Give access to onSuccess and onFailure optionnal handlers to handle
* success or failure.
* onSuccess and onFailure are given operation result as first argument.
*
* @see [source](https://github.com/Poomcha/retrier)
*
* @param callback - Function to retry.
* @param args - Optionnal arguments for callback.
*
*
* @param options - Optionnal options.
*
* @param options.maxRetries - Positive integer, max number of retries <code>(Number[0, MAX_SAFE_INTEGER])</code>.
*
* @param options.delay - Positive integer, delay between 2 retry.
*
* @param options.onSuccess -
* @param options.onSuccess.callback - Function to run on success.
* @param options.onSuccess.args - Optionnal arguments for onSuccess.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onSuccess.override - Optionnal boolean, if true retrySync will return options.onSuccess.callback result.
*
* @param options.Failure -
* @param options.onFailure.callback - Function to run on failure.
* @param options.onFailure.args - Optionnal arguments for onFailure.callback,
* first argument will always be global callback result as <code>[res, ...args]</code>.
* @param options.onFailure.override - Boolean, if true retrySync will return options.onFailure.callback result.
*
*
* @returns callback result or options.onSuccess.callback result or options.onFailure.callback result.
*
*
* @throw callback error or options.onSuccess.callback error or options.onFailure.callback error.
*/
async retryAsync(callback, args, options) {
try {
const computedOptions = this.makeOptionsAsync(
options
);
return await _Retrier._retryAsync(
callback,
computedOptions.maxRetries,
args,
computedOptions
);
} catch (error) {
throw error;
}
}
//#endregion
//#region Private Methods
/**
* Internal method to retry a synchronous operation.
*
* @param callback - The callback function to retry.
* @param retry - The current retry count.
* @param args - Optional arguments to pass to the callback.
* @param options - Optional retry options.
* @returns The result of the callback function.
* @throws Will throw an error if all retries fail.
*/
static _retrySync(callback, retry, args, options) {
let onSuccess;
let onFailure;
if (options) {
onSuccess = options.onSuccess;
onFailure = options.onFailure;
}
try {
const res = UtilsCallback.callSyncCallback(callback, args);
if (onSuccess) {
const onSuccessRes = UtilsCallback.callSyncCallback(
onSuccess.callback,
[res, ...onSuccess.args || []]
);
if (onSuccess.override) {
return onSuccessRes;
}
}
return res;
} catch (error) {
if (retry > 0) {
return _Retrier._retrySync(callback, retry - 1, args, options);
}
if (onFailure) {
const onFailureRes = UtilsCallback.callSyncCallback(
onFailure.callback,
[error, ...onFailure.args || []]
);
if (onFailure.override) {
return onFailureRes;
}
}
throw error;
}
}
/**
* Internal method to retry an asynchronous operation.
*
* @param callback - The callback function to retry.
* @param retry - The current retry count.
* @param args - Optional arguments to pass to the callback.
* @param options - Optional retry options.
* @returns A promise that resolves to the result of the callback function.
* @throws Will throw an error if all retries fail.
*/
static async _retryAsync(callback, retry, args, options) {
let onSuccess;
let onFailure;
if (options) {
onSuccess = options.onSuccess;
onFailure = options.onFailure;
}
try {
const res = await UtilsCallback.callAsyncCallback(callback, args);
if (onSuccess) {
const onSuccessRes = await UtilsCallback.callAsyncCallback(
onSuccess.callback,
[res, ...onSuccess.args || []]
);
if (onSuccess.override) {
return onSuccessRes;
}
}
return res;
} catch (error) {
if (retry > 0) {
if (options && options.delay && options.delay > 0) {
await new Promise((resolve) => {
const timeout = setTimeout(resolve, options.delay);
});
}
return _Retrier._retryAsync(callback, retry - 1, args, options);
}
if (onFailure) {
const onFailureRes = await UtilsCallback.callAsyncCallback(
onFailure.callback,
[error, ...onFailure.args || []]
);
if (onFailure.override) {
return onFailureRes;
}
}
throw error;
}
}
/**
* Internal method to set options configuration for non static sync methods
*
* @param options
*/
makeOptionsSync(options) {
let computedOptions = options;
if (computedOptions) {
if (!computedOptions.maxRetries)
computedOptions.maxRetries = this.getMaxRetries();
else {
try {
UtilsNumber.checkIntegerPositive(computedOptions.maxRetries);
} catch (error) {
throw error;
}
}
if (!computedOptions.onSuccess)
computedOptions.onSuccess = this.getOnSuccess();
if (!computedOptions.onFailure)
computedOptions.onFailure = this.getOnFailure();
} else {
computedOptions = {
maxRetries: this.getMaxRetries(),
onSuccess: this.getOnSuccess(),
onFailure: this.getOnFailure()
};
}
return computedOptions;
}
/**
* Internal method to set options configuration for non static async methods
*
* @param options
*/
makeOptionsAsync(options) {
let computedOptions = options;
if (computedOptions) {
try {
if (!computedOptions.maxRetries)
computedOptions.maxRetries = this.getMaxRetries();
else UtilsNumber.checkIntegerPositive(computedOptions.maxRetries);
if (!computedOptions.delay) computedOptions.delay = this.getDelay();
else UtilsNumber.checkIntegerPositive(computedOptions.delay);
} catch (error) {
throw error;
}
if (!computedOptions.onSuccess)
computedOptions.onSuccess = this.getOnSuccess();
if (!computedOptions.onFailure)
computedOptions.onFailure = this.getOnFailure();
} else {
computedOptions = {
maxRetries: this.getMaxRetries(),
delay: this.getDelay(),
onSuccess: this.getOnSuccess(),
onFailure: this.getOnFailure()
};
}
return computedOptions;
}
//#endregion
};
// index.ts
var retrySync = Retrier.retryStaticSync;
var retryAsync = Retrier.retryStaticAsync;
export {
Retrier,
retryAsync,
retrySync
};