Created
July 25, 2020 22:43
-
-
Save karol-majewski/4f2aa94e7cf5ed6a11db8f411f1d8063 to your computer and use it in GitHub Desktop.
Retry a Promise in TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const retry = (maxAttemps: number) => <T>(thunk: () => Promise<T>): () => Promise<T> => { | |
let attempts = 0; | |
const attempt = async (): Promise<T> => { | |
attempts++; | |
let result: T; | |
try { | |
result = await thunk(); | |
} catch(error) { | |
if (attempts === maxAttemps) { | |
throw error; | |
} | |
result = await attempt() | |
} | |
return result; | |
} | |
return attempt; | |
} |
export function assert(condition: boolean, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
interface Options {
/**
* @default Infinity
*/
maximumAttempts: number;
/**
* @default 0
*/
timeoutMs: number;
/**
* What to do when the allowed retries are exhausted. For example,
* invoke an error boundary by using `useErrorHandler` from 'react-error-boundary'.
*/
handleError: (error: Error) => void;
}
export const retry =
(options: Require<Partial<Options>, 'handleError'>) =>
<T>(thunk: () => Promise<T>): (() => Promise<T>) => {
const { maximumAttempts = Infinity, timeoutMs = 0, handleError } = options;
assert(
Object.is(maximumAttempts, Infinity) || (Number.isInteger(maximumAttempts) && maximumAttempts >= 0),
'Expected maximum allowed number of attempts to be a natural number'
);
assert(timeoutMs >= 0, 'Expected timeoutMs to be zero or a positive number');
let attempts = 0;
const startTime = performance.now();
const attempt = async (): Promise<T> => {
attempts++;
let result: T;
try {
result = await thunk();
} catch (error) {
const endTime = performance.now();
const hasExceededMaximumAttempts = attempts > maximumAttempts;
const hasDefinedTimeout = timeoutMs > 0;
const hasTimedOut = endTime - timeoutMs > startTime;
if (hasExceededMaximumAttempts || (hasDefinedTimeout && hasTimedOut)) {
handleError(error as Error);
}
result = await attempt();
}
return result;
};
return attempt;
};
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: