Javascript: Function that retries with setTimeout
我有一个
这是我到目前为止的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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); } }); } |
显然,这将失败,因为第二次(及以后)我打电话给
我如何使它与第二个承诺一起正常工作?
附言 以防代码在NodeJS中运行。
无需创建新的承诺来处理此问题。假设
1 2 3 4 5 6 7 8 9 10 | 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) ); } |
有些人可能会发现以下稍微干净的东西:
1 2 3 4 5 6 | function downloadItemWithRetryAndTimeout(url, retry) { return function download() { return --retry < 0 ? Promise.reject() : downloadItem(url) . catch(() => wait(1000) . then(download)); }(); } |
我有两个想法:
将promise移出迭代函数downloadItemWithRetryAndTimeout-现在resolve()将可用于所有迭代:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); }); } |
此解决方案有效,但由于它破坏了承诺链,因此是一种反模式:
每次迭代都返回一个承诺时,只需解决该承诺,然后使用.then来解决先前的承诺,依此类推:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } }); } |
@ user663031上的@BenjaminGruenbaum评论很棒,但是有一个小错误,因为:
1 | const delayError = (fn, ms) => fn().catch(e => delay(ms).then(y => Promise.reject(e))) |
实际上应该是:
1 | const delayError = (fn, ms) => () => fn().catch(e => delay(ms).then(y => Promise.reject(e))) |
因此它将返回一个函数,而不是一个promise。这是一个棘手的错误,很难解决,所以我在这里张贴,以防有人需要。整个过程如下:
1 2 3 4 | 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)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); }); } |