Iterative retry
25 December 2023
All the retry()
functions I've found were all recursive, and I had a feeling that it could potentially raise a stack overflow error if the number of attempts was large enough. I validated my concerns via testing with retries of 5000+ returning an error.
/* retry a function n times, with a delay between */
export async function retryRecursive<T>(
fn: () => Promise<T> | T,
attempts: number = 3,
delay: number = 5000,
): Promise<T> {
try {
return await fn();
} catch (error) {
if (attempts < 1) {
throw error;
}
console.log("retrying...");
await sleep(delay);
return retryRecursive(fn, attempts - 1, delay);
}
}
/* Sleep for some time */
export async function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms));
}
So here's an iterative version I wrote instead that doesn't suffer from stack overflow.
/* retry a function n times, with a delay between */
export async function retry<T>(
fn: () => Promise<T> | T,
attempts: number = 3,
delay: number = 5000,
) {
let error;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i < attempts - 1) {
/* not on the last attempt yet */
console.log("retrying...");
await sleep(delay);
}
error = err;
}
}
throw error;
}
/* Sleep for some time */
export async function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms));
}
The reason for setTimeout()
instead of setInterval()
is because "setInterval starts a function every n milliseconds, without any consideration about when a function finishes execution. (source)" It's possible for the function to take longer than the delay, e.g. with a poor network connection, resulting in the delay between attempts being much shorter than expected.
setInterval()
Note that the delay between attempts is not 1 second.
setTimeout()
The delay between attempts is 1 second.