Javascript:使用 setTimeout 重试的函数
Javascript: Function that retries with setTimeout
我有一个功能 downloadItem
可能会因网络原因而失败,我希望能够在实际拒绝该项目之前重试几次。重试需要超时,因为如果出现网络问题,则没有必要立即重试。
这是我目前的情况:
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);
}
});
}
很明显这会失败,因为我第二次(也是)调用 downloadItemWithRetryAndTimeout
我没有 return 按要求做出承诺。
如何让它与第二个承诺一起正常工作?
P.S。如果重要的话,NodeJS 中的代码是 运行。
我有两个想法:
将承诺移出迭代函数 downloadItemWithRetryAndTimeout 的一侧 - 现在 resolve() 将可用于所有迭代:
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);
});
}
此解决方案有效,但它是一种反模式,因为它破坏了承诺链:
作为每个迭代 returns 一个承诺,只需解决承诺,然后使用 .then 解决以前的承诺,依此类推:
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);
}
});
}
function downloadItemWithRetryAndTimeout(url, retry) {
return new Promise(function(resolve, reject) {
var tryDownload = function(attempts) {
try {
downloadItem(url);
resolve();
} catch (e) {
if (attempts == 0) {
reject(e);
} else {
setTimeout(function() {
tryDownload(attempts - 1);
}, 1000);
}
}
};
tryDownload(retry);
});
}
无需创建新的承诺来处理此问题。假设 downloadItem
是同步的并且 return 是一个承诺,只需 return 调用它的结果,以及 catch
递归调用 downloadItemWithRetryAndTimeout
。
function wait(n) { return new Promise(resolve => setTimeout(resolve, n)); }
function downloadItemWithRetryAndTimeout(url, retry) {
if (retry < 0) return Promise.reject();
return downloadItem(url) .
catch(() => wait(1000) .
then(() => downloadItemWithRetryAndTimeout(url, retry - 1)
);
}
有些人可能会觉得以下内容更简洁:
function downloadItemWithRetryAndTimeout(url, retry) {
return function download() {
return --retry < 0 ? Promise.reject() :
downloadItem(url) . catch(() => wait(1000) . then(download));
}();
}
@BenjaminGruenbaum 对@user663031 的评论很棒,但有一个小错误,因为:
const delayError = (fn, ms) => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
实际上应该是:
const delayError = (fn, ms) => () => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
所以它将 return 一个功能,而不是一个承诺。这是一个很难解决的棘手错误,所以我在这里发帖以防万一有人需要它。全部内容如下:
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))
我有一个功能 downloadItem
可能会因网络原因而失败,我希望能够在实际拒绝该项目之前重试几次。重试需要超时,因为如果出现网络问题,则没有必要立即重试。
这是我目前的情况:
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);
}
});
}
很明显这会失败,因为我第二次(也是)调用 downloadItemWithRetryAndTimeout
我没有 return 按要求做出承诺。
如何让它与第二个承诺一起正常工作?
P.S。如果重要的话,NodeJS 中的代码是 运行。
我有两个想法:
将承诺移出迭代函数 downloadItemWithRetryAndTimeout 的一侧 - 现在 resolve() 将可用于所有迭代:
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);
});
}
此解决方案有效,但它是一种反模式,因为它破坏了承诺链: 作为每个迭代 returns 一个承诺,只需解决承诺,然后使用 .then 解决以前的承诺,依此类推:
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);
}
});
}
function downloadItemWithRetryAndTimeout(url, retry) {
return new Promise(function(resolve, reject) {
var tryDownload = function(attempts) {
try {
downloadItem(url);
resolve();
} catch (e) {
if (attempts == 0) {
reject(e);
} else {
setTimeout(function() {
tryDownload(attempts - 1);
}, 1000);
}
}
};
tryDownload(retry);
});
}
无需创建新的承诺来处理此问题。假设 downloadItem
是同步的并且 return 是一个承诺,只需 return 调用它的结果,以及 catch
递归调用 downloadItemWithRetryAndTimeout
。
function wait(n) { return new Promise(resolve => setTimeout(resolve, n)); }
function downloadItemWithRetryAndTimeout(url, retry) {
if (retry < 0) return Promise.reject();
return downloadItem(url) .
catch(() => wait(1000) .
then(() => downloadItemWithRetryAndTimeout(url, retry - 1)
);
}
有些人可能会觉得以下内容更简洁:
function downloadItemWithRetryAndTimeout(url, retry) {
return function download() {
return --retry < 0 ? Promise.reject() :
downloadItem(url) . catch(() => wait(1000) . then(download));
}();
}
@BenjaminGruenbaum 对@user663031 的评论很棒,但有一个小错误,因为:
const delayError = (fn, ms) => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
实际上应该是:
const delayError = (fn, ms) => () => fn().catch(e => delay(ms).then(y => Promise.reject(e)))
所以它将 return 一个功能,而不是一个承诺。这是一个很难解决的棘手错误,所以我在这里发帖以防万一有人需要它。全部内容如下:
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))