refactor(core/stdlib): improve retry function implementation and error handling
This commit is contained in:
@@ -9,35 +9,40 @@ export interface RetryOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RetryFunction<Return> = (
|
export type RetryFunction<Return> = (
|
||||||
args: {
|
args: {
|
||||||
count: number;
|
count: number;
|
||||||
stop: (error: any) => void;
|
stop: (error: any) => void;
|
||||||
},
|
},
|
||||||
) => Promise<Return>;
|
) => Promise<Return>;
|
||||||
|
|
||||||
const RetryEarlyExit = Symbol('RetryEarlyExit');
|
class RetryEarlyExitError {
|
||||||
|
cause: any;
|
||||||
|
constructor(cause: any) {
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name retry
|
* @name retry
|
||||||
* @category Async
|
* @category Async
|
||||||
* @description Retries a function a specified number of times with a delay between each retry
|
* @description Retries a function a specified number of times with a delay between each retry
|
||||||
*
|
*
|
||||||
* @param {Promise<unknown>} fn - The function to retry
|
* @param {Promise<unknown>} fn - The function to retry
|
||||||
* @param {RetryOptions} options - The options for the retry
|
* @param {RetryOptions} options - The options for the retry
|
||||||
* @returns {Promise<unknown>} - The result of the function
|
* @returns {Promise<unknown>} - The result of the function
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const result = await retry(() => {
|
* const result = await retry(() => {
|
||||||
* return fetch('https://jsonplaceholder.typicode.com/todos/1')
|
* return fetch('https://jsonplaceholder.typicode.com/todos/1')
|
||||||
* .then(response => response.json())
|
* .then(response => response.json())
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const result = await retry(() => {
|
* const result = await retry(() => {
|
||||||
* return fetch('https://jsonplaceholder.typicode.com/todos/1')
|
* return fetch('https://jsonplaceholder.typicode.com/todos/1')
|
||||||
* .then(response => response.json())
|
* .then(response => response.json())
|
||||||
* }, { times: 3, delay: 1000 });
|
* }, { times: 3, delay: 1000 });
|
||||||
*
|
*
|
||||||
* @since 0.0.8
|
* @since 0.0.8
|
||||||
*/
|
*/
|
||||||
export async function retry<Return>(
|
export async function retry<Return>(
|
||||||
@@ -50,24 +55,25 @@ export async function retry<Return>(
|
|||||||
shouldRetry,
|
shouldRetry,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
const wrappedFn = tryIt(fn);
|
||||||
|
const delayFn = isFunction(delay) ? delay : null;
|
||||||
|
const delayMs = delayFn ? 0 : delay as number;
|
||||||
|
|
||||||
|
const stop = (error?: any): never => {
|
||||||
|
throw new RetryEarlyExitError(error);
|
||||||
|
};
|
||||||
|
|
||||||
|
let lastError: Error | null = null;
|
||||||
let count = 1;
|
let count = 1;
|
||||||
let lastError: Error = new Error('Retry failed');
|
|
||||||
|
|
||||||
while (count <= times) {
|
while (count <= times) {
|
||||||
const metadata = {
|
const { error, data } = await wrappedFn({ count, stop });
|
||||||
count,
|
|
||||||
stop: (error?: any) => {
|
|
||||||
throw { [RetryEarlyExit]: error };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { error, data } = await tryIt(fn)(metadata);
|
|
||||||
|
|
||||||
if (!error)
|
if (!error)
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
if (RetryEarlyExit in error)
|
if (error instanceof RetryEarlyExitError)
|
||||||
throw error[RetryEarlyExit];
|
throw error.cause;
|
||||||
|
|
||||||
if (shouldRetry && !shouldRetry(error, count))
|
if (shouldRetry && !shouldRetry(error, count))
|
||||||
throw error;
|
throw error;
|
||||||
@@ -77,12 +83,12 @@ export async function retry<Return>(
|
|||||||
|
|
||||||
// Don't delay after the last attempt
|
// Don't delay after the last attempt
|
||||||
if (count <= times) {
|
if (count <= times) {
|
||||||
const delayMs = isFunction(delay) ? delay(count) : delay;
|
const ms = delayFn ? delayFn(count) : delayMs;
|
||||||
|
|
||||||
if (delayMs > 0)
|
if (ms > 0)
|
||||||
await sleep(delayMs);
|
await sleep(ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw lastError;
|
throw lastError!;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user