Skip to content

Instantly share code, notes, and snippets.

@simonsmith
Last active January 17, 2024 22:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonsmith/0ffc14b716288fbb9b74fb62afc39950 to your computer and use it in GitHub Desktop.
Save simonsmith/0ffc14b716288fbb9b74fb62afc39950 to your computer and use it in GitHub Desktop.
Retries a Promise until it resolves or the retries are exceeded
import {retry} from './retry';
jest.spyOn(global, 'setTimeout');
test('retries a function and increases the backoff for each failure', async () => {
const rejectingFunc = jest.fn(
(() => {
let failCount = 5;
return () => {
if (failCount === 0) {
return Promise.resolve('resolved');
}
failCount -= 1;
return Promise.reject(`failed`);
};
})()
);
await retry(rejectingFunc, {
retries: 6,
delay: 10,
backOff: 20,
});
expect(setTimeout).toHaveBeenCalledTimes(5);
expect(setTimeout).toHaveBeenNthCalledWith(1, expect.any(Function), 10);
expect(setTimeout).toHaveBeenNthCalledWith(2, expect.any(Function), 30);
expect(setTimeout).toHaveBeenNthCalledWith(3, expect.any(Function), 50);
expect(setTimeout).toHaveBeenNthCalledWith(4, expect.any(Function), 70);
expect(setTimeout).toHaveBeenNthCalledWith(5, expect.any(Function), 90);
});
test('throws an error when retries are exceeded', async () => {
const rejectingFunc = jest.fn(
(() => {
let failCount = 5;
return () => {
if (failCount === 0) {
return Promise.resolve('resolved');
}
failCount -= 1;
return Promise.reject(`failed`);
};
})()
);
await expect(
retry(rejectingFunc, {
retries: 2,
delay: 10,
backOff: 20,
})
).rejects.toEqual(new Error('exceeded retries'));
});
export const retry = <T>(
funcToRetry: () => Promise<T>,
{
retries = 5,
delay = 1000,
backOff = 0,
}: {
retries?: number
delay?: number
backOff?: number
} = {},
) => {
const wait = (delay: number) =>
new Promise((resolve) => setTimeout(resolve, delay))
const handleError = (): Promise<T> => {
retries -= 1
if (retries === 0) {
throw new Error('exceeded retries')
}
console.log(
`retry.ts retries remaining: ${retries}.\nnext attempt in ${delay}ms`,
)
return wait(delay).then(() =>
retry(funcToRetry, {delay: delay + backOff, retries, backOff}),
)
}
return funcToRetry().catch(handleError)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment