Javascript:使用setTimeout重试的函数
我有一个函数Javascript:使用setTimeout重试的函数,javascript,node.js,promise,settimeout,Javascript,Node.js,Promise,Settimeout,我有一个函数downloadItem,由于网络原因可能会失败,我希望能够在实际拒绝该项之前重试几次。重试需要超时,因为如果存在网络问题,则没有必要立即重试 以下是我到目前为止的情况: function downloadItemWithRetryAndTimeout(url, retry, failedReason) { return new Promise(function(resolve, reject) { try { if (retry &l
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(url、重试、失败原因){
返回新承诺(功能(解决、拒绝){
试一试{
如果(重试<0&&failedReason!=null)拒绝(failedReason);
下载项目(url);
解决();
}捕获(e){
setTimeout(函数(){
downloadItemWithRetryAndTimeout(url,重试-1,e);
}, 1000);
}
});
}
显然,这将失败,因为在我第二次调用DownloadItemWithRetry和Timeout时,我没有按要求返回承诺
我如何使它正确地与第二个承诺一起工作
另外,如果代码在NodeJS中运行很重要。我有两个想法:
将迭代函数downloadItemWithRetryAndTimeout的promise移出,此时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);
});
}
函数下载包装器(url,重试){
返回新承诺(功能(解决、拒绝){
函数downloadItemWithRetryAndTimeout(url、重试、失败原因){
试一试{
如果(重试<0&&failedReason!=null)
拒绝(失败原因);
下载项目(url);
解决();
}捕获(e){
setTimeout(函数(){
downloadItemWithRetryAndTimeout(url,重试-1,e);
}, 1000);
}
}
downloadItemWithRetryAndTimeout(url,重试,null);
});
}
此解决方案可行,但它是一种反模式,因为它打破了承诺链:
由于每次迭代都会返回一个承诺,所以只需解析该承诺,然后使用。然后解析之前的承诺,依此类推:
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);
}
});
}
函数downloadItemWithRetryAndTimeout(url、重试、失败原因){
返回新承诺(功能(解决、拒绝){
试一试{
如果(重试<0&&failedReason!=null)
拒绝(失败原因);
下载项目(url);
解决();
}捕获(e){
setTimeout(函数(){
downloadItemWithRetryAndTimeout(url,重试-1,e)。然后(函数(){
解决();
});
}, 1000);
}
});
}
无需创建新的承诺来处理此问题。假设downloadItem
是同步的并返回一个承诺,只需返回调用它的结果,以及一个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 wait(n){返回新承诺(resolve=>setTimeout(resolve,n));}
函数downloadItemWithRetryAndTimeout(url,重试){
if(重试<0)返回Promise.reject();
返回下载项(url)。
捕获(()=>等待(1000)。
然后(()=>downloadItemWithRetryAndTimeout(url,重试-1)
);
}
有些人可能会发现以下情况稍微好一些:
function downloadItemWithRetryAndTimeout(url, retry) {
return function download() {
return --retry < 0 ? Promise.reject() :
downloadItem(url) . catch(() => wait(1000) . then(download));
}();
}
函数downloadItemWithRetryAndTimeout(url,重试){
返回函数下载(){
return--重试<0?承诺.拒绝():
下载项目(url).catch(()=>wait(1000).then(下载));
}();
}
@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)))
所以它将返回一个函数,而不是一个承诺。这是一个棘手的错误,很难解决,所以我在这里发布,以防任何人需要它。下面是全部内容:
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))
const retry=(fn,retries=3)=>fn()
const delayError=(fn,ms)=>()=>fn().catch(e=>delay(ms).然后(y=>Promise.reject(e)))
重试(延迟错误(下载,1000))
也许使用承诺不适合你的场景。我不明白为什么人们突然对每一个异步案例使用承诺。我不必使用承诺,你有没有没有没有承诺的解决方案?@webduvet:因为这就是承诺的目的:只有一个结果的异步案例。它非常适合这个场景。@AlexD:你的downloadItem
函数是否真的同步?我认为它应该返回一个承诺。@alexd with Bluebird您可以使用,我真的很喜欢您的第一个解决方案!在声明内部函数后,我在承诺中对它做了一些更改,您也必须调用它一次。我现在就试试。邋遢的我-忘记运行函数-修复。请避免@Bergi-谢谢。我不认为它会落入同一个延迟陷阱。通过一个通用的重试方法来做这件事会更有意义。const retry=(fn,retries=3)=>fn().catch(e=>retries new Promise(r=>setTimeout(r,ms))
和const delayError=(fn,ms)=>fn().catch(e=>delay(ms)。然后(y=>Promise.reject(e)))
。然后代码变成const downloadWithRetryAndTimeout=retry(delayError(download,1000))
或类似的东西。@BenjaminGruenbaum感谢这个优雅的分解。
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))