Javascript: Function that retries with setTimeout
Solution 1
I've got two ideas:
Move the promise out side of the iterated function downloadItemWithRetryAndTimeout - now resolve() will available to all iterations:
function downloadWrapper(url, retry) {
return new Promise(function (resolve, reject) {
function downloadItemWithRetryAndTimeout(url, retry, failedReason) {
try {
if (retry < 0 && failedReason != null)
reject(failedReason);
downloadItem(url);
resolve();
} catch (e) {
setTimeout(function () {
downloadItemWithRetryAndTimeout(url, retry - 1, e);
}, 1000);
}
}
downloadItemWithRetryAndTimeout(url, retry, null);
});
}
This solution works, but it's an anti pattern as it breaks the promise chain: As each iteration returns a promise, just resolve the promise, and use .then to resolve the previous promise, and so on:
function downloadItemWithRetryAndTimeout(url, retry, failedReason) {
return new Promise(function (resolve, reject) {
try {
if (retry < 0 && failedReason != null)
reject(failedReason);
downloadItem(url);
resolve();
} catch (e) {
setTimeout(function () {
downloadItemWithRetryAndTimeout(url, retry - 1, e).then(function () {
resolve();
});
}, 1000);
}
});
}
Solution 2
@BenjaminGruenbaum comment on @user663031 is fantastic but there's a slight error because this:
const delayError = (fn, ms) => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
should actually be:
const delayError = (fn, ms) => () => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
so it will return a function, not a promise. It's a tricky error that's hard to solve so I'm posting on here in case anyone needs it. Here's the whole thing:
const retry = (fn, retries = 3) => fn().catch(e => retries <= 0 ? Promise.reject(e) : retry(fn, retries - 1))
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const delayError = (fn, ms) => () => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
retry(delayError(download, 1000))
Comments
-
AlexD almost 2 years
I have a function
downloadItem
that may fail for network reasons, I want to be able to retry it a few times before actually rejecting that item. The retries need to be with a timeout since if there is a network issue there is no point in retrying immediately.Here is what I have so far:
function downloadItemWithRetryAndTimeout(url, retry, failedReason) { return new Promise(function(resolve, reject) { try { if (retry < 0 && failedReason != null) reject(failedReason); downloadItem(url); resolve(); } catch (e) { setTimeout(function() { downloadItemWithRetryAndTimeout(url, retry - 1, e); }, 1000); } }); }
Obviously this will fail since the second (and on) time I call
downloadItemWithRetryAndTimeout
I don't return a promise as required.How do I make it work correctly with that second promise?
P.S. incase it matters the code is running in NodeJS.