[removed] Function that retries with setTimeout

前端 未结 4 1066
灰色年华
灰色年华 2021-01-03 07:16

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 nee

相关标签:
4条回答
  • 2021-01-03 07:47

    There is no need to create new promises to handle this. Assuming downloadItem is synchronous and returns a promise, simply return the result of calling it, along with a catch to call downloadItemWithRetryAndTimeout recursively.

    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)
      );
    }
    

    Some might find the following slightly cleaner:

    function downloadItemWithRetryAndTimeout(url, retry) {
      return function download() {
        return --retry < 0 ? Promise.reject() :
          downloadItem(url) . catch(() => wait(1000) . then(download));
      }();
    }
    
    0 讨论(0)
  • 2021-01-03 07:57
    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);
        });
    }
    
    0 讨论(0)
  • 2021-01-03 08:01

    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);
            }
        });
    }
    
    0 讨论(0)
  • 2021-01-03 08:01

    @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))
    
    0 讨论(0)
提交回复
热议问题