ts-retry
A little retry tool to execute a function until the function is successful. Can also bind a timeout to a function.
This lib is usable in typescript, in javascript, in node, in SPA tools (rest, Vue, Svelte...) and browser (available in ESM and common js format).
Breaking change: For those who are using 1.x in typescript, you may have to add a type to RetryOptions if you want to use
the new until
function. This type is the called function returns type.
How to:
-
to retry something:
const result = await retry(
() => {
},
{ delay: 100, maxTry: 5 }
);
-
to retry something async :
const result = await retryAsync(
async () => {
},
{ delay: 100, maxTry: 5 }
);
-
to retry until the answer is 42 :
try {
await retryAsync(
async () => {
},
{
delay: 100,
maxTry: 5,
until: (lastResult) => lastResult === 42,
}
);
} catch (err) {
if (isTooManyTries(err)) {
} else {
}
}
-
Need to call a function at multiple locations with same retryOptions ? Use decorators:
const fn = (param1: string, param2:number) => ;
const decoratedFn = retryDecorator(
fn,
{ delay:100, maxTry:5 }
);
const title1 = await decoratedFn("value1", 1);
const title2 = await decoratedFn("valueXXX", 2);
const fn = async (name: string): Promise<any> => { };
const decoratedFn = retryAsyncDecorator(
fn,
{ delay:100, maxTry:5 }
);
const result1 = await decoratedFn("Smith");
const result2 = await decoratedFn("Doe");
-
to wait:
await wait(10000);
-
to set a timeout:
try {
const result = await waitUntil(
async ()=> {},
10000
);
} catch (err) {
if (isTimeoutError(error)) { {
} else {
}
}
-
to set a timeout on something async:
try {
const result = await waitUntilAsync(async () => {
}, 10000);
} catch (err) {
if (isTimeoutError(error)) {
} else {
}
}
-
Need to call a function at multiple locations with same retryOptions ? Use decorators:
const fn = (title: string, count:number) => ;
const decoratedFn = waitUntilDecorator(
fn,
{ delay:100, maxTry:5 }
);
const title1 = await decoratedFn("Intro", 1);
const title2 = await decoratedFn("A chapter", 2);
const fn = async (name: string): Promise<any> => {
};
const decoratedFn = waitUntilAsyncDecorator(fn, { delay: 100, maxTry: 5 });
const result1 = await decoratedFn("John");
const result2 = await decoratedFn("Doe");
Utils
retry
comes with handy utilities function for common use case:
- to retry until a function returns something defined (aka not null neither not undefined):
const result = await retryUntilDefined( (): string|undefined => { ... } ) );
const result = await retryUntilAsyncDefined( (): Promise<string|null> => { ... } );
const decorated = retryUntilDefinedDecorator( (p1: string): string|undefined => { ... } );
const result = await decorated('hello world');
const decorated = retryAsyncUntilDefinedDecorator( (p1: string): Promise<string|undefined> => { ... } );
const result = await decorated('hello world');
- to retry until a function returns something truthy:
const result = await retryUntilTruthy( (): boolean|undefined => { ... } ) );
const result = await retryAsyncUntilTruthy( (): Promise<number|null> => { ... } );
const decorated = retryUntilTruthyDecorator( (p1: string): boolean|undefined => { ... } );
const result = await decorated('hello world');
const decorated = retryAsyncUntilTruthyDecorator( (p1: string): Promise<boolean|null> => { ... } );
const result = await decorated('hello world');
- to retry until fetch is successfully:
const result = await retryAsyncUntilResponse( () => fetch(...) );
const decorated = retryAsyncUntilResponseDecorator( (param) => fetch(...) );
const result = await decorated('q=1');
API
Retry family
if (isTooManyTries(error)) {
}
Wait family
wait(duration?)
: Do nothing during "duration" millisecondswaitUntil(fn, duration?, error?)
: waitUntil call asynchronously fn once. If fn complete within the duration (express in milliseconds), waitUntil returns the fn result. Otherwise, it throws the given error (if any) or a TimeoutError exception.waitUntilAsync(fn, duration?, error?)
: same as waitUntil, except fn is an asynchronous function.TimeoutError
: an error thrown by waitUntil and waitUntilAsync. It comes with a isTimeoutError type guard:
if (isTimeoutError(error)) {
}
In case of timeout fn is still executing. It is advised to add a mean to abort it.
- When duration is not provided, the default one is applied. The default is 60000ms.
setDefaultDuration(duration: number)
: change the default duration.getDefaultDuration()
: returns the current default duration.waitUntilAsyncDecorator(fn: T, duration?: number, error?: Error)
and waitUntilDecorator(fn: T, duration?: number, error?: Error)
: decorators that return a function with same signature than the given function. On decorated call, fn is called bounded to the duration.
Custom reaction when max retry is achieved
Sometimes, you need to perform some actions when max retry has achieved and the error is still there. For this onMaxRetryFunc?: (err: Error) => void;
optional function was added to RetryOptions
.
For example, you would like to store results of the error into the file in order to process it later. Here's how you can do it :
export const runWithRetry = <T>(
message: string,
serviceUnderTest: ServiceUnderTest,
fn: () => T | Promise<T>,
delay = 1000,
maxTry = 10
) => {
const saveErrorReport = (err) => {
const errorDetails = {
serviceName: serviceUnderTest.connectorName,
error: err.message as string,
description: `Failed to ${message} because of ${err.message as string}`,
errorName: err.name as string,
stack: err.stack as string,
};
const path = resolve(
__dirname,
`../../../failed-service-report/${serviceUnderTest.connectorName}.json`
);
writeFile(path, Buffer.from(JSON.stringify(errorDetails)));
};
return retryAsync(
async () => {
logger.info(`${serviceUnderTest.description}: ${message}`);
return fn();
},
{
delay,
maxTry,
onMaxRetryFunc: saveErrorReport,
}
);
};
Utils family
retry
comes with handy utilities function for common use case:
UntilDefined :
To retry until we get a value which is neither null nor undefined.
For calling sync function:
retryUntilDefined<RETURN_TYPE>(
fn: () => RETURN_TYPE | undefined | null,
retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryUntilDefinedDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE | undefined | null,
retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>
For calling async function:
retryAsyncUntilDefined<RETURN_TYPE>(
fn: () => Promise<RETURN_TYPE | undefined | null>,
options?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilDefinedDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE | undefined | null>,
retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>
UntilTruthy :
To retry until we get a value which javascript consider as truthy.
For calling sync function:
retryUntilTruthy<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE,
retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryUntilTruthyDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE,
retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>
For calling async function:
retryAsyncUntilTruthy<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilTruthyDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>
UntilResponse :
To retry until fetch is sucessfull.
retryAsyncUntilResponse<PARAMETERS_TYPE, RETURN_TYPE extends { ok: boolean }>(
fn: () => Promise<RETURN_TYPE>,
retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilResponseDecorator<PARAMETERS_TYPE, RETURN_TYPE extends { ok: boolean }>(
fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>
RetryUtilsOptions
type is:
maxTry
: [optional] maximum calls to fn.delay
: [optional] delay between each call (in milliseconds).until
: [optional] function that receive the result of a try. retries are stopped when this function return trueonMaxRetryFunc
: [optional]: function called when maximum retries has been reached.
When not provided, maxTry and delay of global options are applied.
Compatibility
This lib works with Deno (to import it,use the url https://raw.githubusercontent.com/franckLdx/ts-retry/<version>/src/index.ts
). However, it's more convenient to use the specific port of this lib to Deno: see https://deno.land/x/retry