@ugo-code/streamline.js
Version:
A utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript
80 lines (79 loc) • 3.61 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.retry = retry;
// --- Default Configuration Constants ---
const DEFAULT_LIMIT = 2;
const DEFAULT_INITIAL_DELAY = 0;
const DEFAULT_MAX_DELAY = Infinity;
/**
* Executes an asynchronous task and automatically retries it if it fails.
* By default, retries happen immediately without any delay. An exponential
* backoff delay can be enabled by setting the `initialDelay` option.
*
* This function is useful for handling transient errors in network requests or
* other operations that might succeed on a subsequent attempt.
*
* @template TResult The expected result type of the asynchronous task.
* @param {() => Promise<TResult>} taskFn The asynchronous function to execute.
* This function should not take any arguments and must return a Promise.
* @param {RetryOptions<TResult>} [options={}] Optional configuration to control
* the retry behavior, such as the number of retries and delay timings.
* @returns {Promise<TResult>} A promise that either resolves with the task's
* successful result or rejects with the last error encountered after all
* attempts have been exhausted.
* @example
* ```ts
* // Example 1: Basic usage with default settings (2 retries, no delay)
* const data = await retryTask(fetchData);
*
* // Example 2: Customizing retry behavior to include a delay
* const user = await retryTask(fetchUser, {
* limit: 3,
* initialDelay: 100, // Enable a 100ms initial delay with backoff
* onRetry: (error, attempt) => {
* console.log(`Attempt ${attempt} failed. Retrying in a moment...`, error);
* }
* });
* ```
*/
function retry(taskFn_1) {
return __awaiter(this, arguments, void 0, function* (taskFn, options = {}) {
const { limit = DEFAULT_LIMIT, initialDelay = DEFAULT_INITIAL_DELAY, maxDelay = DEFAULT_MAX_DELAY, onRetry, } = options;
let lastError;
for (let attempt = 0; attempt <= limit; attempt++) {
try {
return yield taskFn();
}
catch (error) {
lastError = error;
if (attempt === limit) {
break;
}
const exponentialDelay = initialDelay * Math.pow(2, attempt);
const delay = Math.min(exponentialDelay, maxDelay);
if (typeof onRetry === "function") {
try {
onRetry(error, attempt + 1, delay);
}
catch (onRetryError) {
console.error("Error within onRetry callback:", onRetryError);
}
}
// Only introduce a delay if it's greater than 0.
if (delay > 0) {
yield new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
throw lastError;
});
}