Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 3x 3x 3x 3x 3x 4x 4x 4x 4x 4x 10x 10x 8x 8x 8x 8x 6x 6x 6x 6x 6x 2x | import { loggerFactory } from "../logger";
import { stringifyError } from "./errors";
import { sleep } from "./time";
export type RetryConfig<T extends (...args: unknown[]) => Promise<unknown>> = {
fn: T;
fnName?: string;
maxRetries: number;
waitBetweenMs?: number;
disableLog?: boolean;
backOff?: {
backOffBase: number;
};
};
const logger = loggerFactory("retry");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function retry<T extends (...args: any[]) => Promise<unknown>>(
config: RetryConfig<T>
) {
Iif (config.maxRetries === 0) {
throw new Error(
`Setting 'config.maxRetries' to 0 will never call the underlying function`
);
}
return async (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {
const fnName = config.fnName ?? config.fn.name;
const error = new AggregateError(
[],
`Retry failed after ${config.maxRetries} attempts of ${fnName}`
);
for (let i = 0; i < config.maxRetries; i++) {
try {
return await (config.fn(...args) as Promise<ReturnType<T>>);
} catch (e) {
error.errors.push(e);
if (!config.disableLog) {
logger.log(
`Retry ${i + 1}/${config.maxRetries}; Function ${fnName} failed.`,
stringifyError(e)
);
}
// don't wait in the last iteration
if (config.waitBetweenMs && i !== config.maxRetries - 1) {
const sleepTimeBackOffMultiplier = config.backOff
? Math.pow(config.backOff.backOffBase, i)
: 1;
const sleepTime = config.waitBetweenMs * sleepTimeBackOffMultiplier;
if (!config.disableLog) {
logger.log(`Waiting ${sleepTime / 1000} s. for the next retry...`);
}
await sleep(sleepTime);
}
}
}
throw error;
};
}
|