async-wrappers
Version:
A set of wrapper functions to perform debouncing, throttling, retrying etc.
235 lines (197 loc) • 5.02 kB
JavaScript
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import wait from './wait';
/**
* @internal
*/
var defaultDelays = [// 10 seconds
10 * 1000, // 1 Minute
60 * 1000, // 5 Minutes
5 * 60 * 1000, // 10 Minutes
10 * 60 * 1000];
/**
* Function for determining a how long to wait after the given attempt
*
* @category Retry
*/
/**
* @internal
*/
var getDelayFn = times => {
if (typeof times === 'function') {
return times;
}
if (typeof times === 'number') {
return () => times;
}
return attempt => times[Math.min(attempt, times.length - 1)];
};
/**
* A function used to determine if to stop for the given retry attempt.
*
* Errors thrown by this function will cause retry to reject with the
* errorTypes of retry errors
*
* @category Retry
*/
/**
* @internal
*/
var getStopFn = stop => {
if (typeof stop === 'function') {
return stop;
}
return attempt => attempt >= stop;
};
/**
* callback for [[retry]] to determine the arguments to
* the function to retry.
* @typeparam RetryFunction the type of function to retry.
*
* @category Retry
*/
/**
* @internal
*/
var getArgsFn = args => {
if (typeof args === 'function') {
return args;
}
return () => args;
};
/**
* Types of retry errors
*
* @category Retry
*/
/**
* Base Class for all retry errors
*
* @category Retry
*/
export class RetryError extends Error {
/**
* The type of error
*/
/**
* The last error that occurred when retrying
*/
constructor(type, message, error) {
super(message);
this.type = type;
this.error = error;
}
}
/**
* The error given when retrying is cancelled
*
* @category Retry
*/
export class RetryCancelledError extends RetryError {
constructor() {
var message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Cancelled';
var error = arguments.length > 1 ? arguments[1] : undefined;
super('cancelled', message, error);
}
}
/**
* The error given when retrying stops
*
* @category Retry
*/
export class RetryStoppedError extends RetryError {
constructor() {
var message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Stopped';
var error = arguments.length > 1 ? arguments[1] : undefined;
super('stopped', message, error);
}
}
/**
* The return type of [[retry]]
*
* @category Retry
*/
/**
* Retry the wrapped function according to the
*
* @param func the function to retry.
* @param delay a delay in milliseconds, an array of millisecond delays or
* [[RetryDelayCallback|callback]] to determine the delay before the next
* attempt.
*
* @param stop the number of attempts, or [[RetryStopCallback|callback]] to
* determine when retry should stop retrying `func`.
* @param args an array or [[RetryArgumentsCallback|callback]] to
* provide arguments to `func`
*
* @category Wrapper
*/
var retry = function retry(func) {
var delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultDelays;
var stop = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
var args = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
var getNextDelay = getDelayFn(delay);
var shouldStop = getStopFn(stop);
var getArgs = getArgsFn(args);
var attempt = 0;
var finished = false;
var cancelled = false;
var cancelReason;
var waiting;
var previousDelay = 0;
var currentError;
var afterAwait = () => {
if (finished || !cancelled) {
return;
}
throw new RetryCancelledError(cancelReason, currentError);
};
var exec =
/*#__PURE__*/
function () {
var _ref = _asyncToGenerator(function* () {
while (!cancelled) {
var _args = yield getArgs(attempt, previousDelay, currentError);
afterAwait();
try {
var value = yield func(..._args);
finished = true;
return value;
} catch (error) {
currentError = error;
afterAwait(); // how long to wait after the last attempt
var _delay = yield getNextDelay(attempt, previousDelay, error);
afterAwait();
previousDelay = _delay;
attempt++;
var _stop = yield shouldStop(attempt, _delay, error);
afterAwait(); // should we stop the next attempt?
if (_stop) {
var _reason = typeof _stop === 'string' ? _stop : undefined;
finished = true;
throw new RetryStoppedError(_reason, error);
}
waiting = wait(_delay);
yield waiting;
waiting = undefined;
afterAwait();
}
}
});
return function exec() {
return _ref.apply(this, arguments);
};
}();
var result = exec();
result.cancel = reason => {
if (finished || cancelled) {
return;
}
cancelled = true;
cancelReason = reason;
if (waiting) {
waiting.stop();
}
};
return result;
};
export default retry;