我有一个函数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
的第二个(及时)我没有按要求返回承诺。
如何让它与第二个承诺一起正常工作?
P.S。因为代码在NodeJS中运行很重要。
答案 0 :(得分:3)
没有必要创建新的承诺来处理这个问题。假设downloadItem
是同步的并且返回一个promise,只需返回调用它的结果,并返回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));
}();
}
答案 1 :(得分:1)
我有两个想法:
移动迭代函数downloadItemWithRetryAndTimeout的promise out - 现在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);
});
}
这个解决方案有效,但它违反了承诺链,它是一种反模式: 当每次迭代返回一个promise时,只需解析promise,然后使用.then解析之前的promise,依此类推:
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);
}
});
}
答案 2 :(得分:1)
@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)))
因此它将返回一个函数,而不是一个Promise。这是一个棘手的错误,很难解决,所以我在这里张贴,以防有人需要。这就是整个事情:
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))
答案 3 :(得分:0)
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);
});
}