Last active
January 17, 2024 22:22
-
-
Save simonsmith/0ffc14b716288fbb9b74fb62afc39950 to your computer and use it in GitHub Desktop.
Retries a Promise until it resolves or the retries are exceeded
This file contains 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
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')); | |
}); |
This file contains 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
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