首页 > 解决方案 > 原生 JS Promise 重试包装器

问题描述

问题概述: 我在 FireFox 中有一个GreaseMonkey / TamperMonkey脚本,它向 URL 发送多个GM_xmlhttpRequest本质上类似于 xml http 请求,但是这个包装器允许轻松进行跨域请求)以检索简单的 JSON 数据。检索到的一些 JSON 数据非常简单,就像这样,

    {id:123459876, code:"frog"}
    [{value:"water", contents:0}]

这使我概述了到目前为止我为我的编程任务编写的代码,并最终形成了我最终要问的问题。

问题大纲: 由于我本质上正在做的工作流程如下;(基于这个SO问题/答案

  1. 在对象内部发送'GET'请求GM_xmlhttpRequestPromise
function makeRequest(reqMethod, navURL) {
    return new Promise(function (resolve, reject) {
        GM_xmlhttpRequest({
            method: reqMethod,
            url:    navURL,
            onload: function () {
                if (this.status >= 200 && this.status < 300) {
                    resolve(this.response);
                } else {
                    reject({
                        status: this.status,
                        statusText: this.statusText
                    });
                }
            },
            onprogress: function(event) {
                console.log(`Loaded ${event.loaded} of ${event.total}`);
            },
            onerror: function(data) {
                reject({
                    status: this.status,
                    statusText: this.statusText
                });
            }
        });
    });
}
  1. GM_xmlhttpRequest- 中返回成功状态后,promise 将 JSON 作为解析响应返回。
    function makeRequestWrapper(requestMethod, navToURL, successFunction) {
        makeRequest(requestMethod, navToURL)
            .then(
                function (datums) {
                    successFunction(datums);
                }
            )
            .catch(
                function (err) {
                    console.error('there was an error!', err.statusText);
                }
            );
    }
  1. 拥有初始承诺,在成功解决后,运行后处理函数以使用检索到的 JSON 数据更新当前窗口页面
// one of the 3 example handler methods for dealing with the success + JSON
    function successHandler1(jsonDataResponse) {
        let response = JSON.parse(jsonDataResponse);
        let id = response[0].id;
        let updateLink = document.getElementById("unique_link_id").parentNode.parentNode;
        updateLink .innerHTML += ` ID: ${id} `;
    }

到目前为止,我的代码按预期工作,Promise on success 返回 JSON 数据,我用检索到的数据更新当前窗口页面。

但是,我的问题是,提取此 JSON 数据的脚本的某些用户会遇到 Promise 失败(有些人比其他人更频繁) - 因此我正在尝试的下一步分类是重试 Promise 一定次数,并且,延迟。这样,我认为重试失败的 Promise,假设 3 次,延迟 3 秒,应该有足够的尝试和时间来提高 Promise 的成功率。

我的想法

由于我是 JS 的相对新手(仍在学习它并希望成为我可以在生活中使用的语言),我读过诸如Promises 和 Observables 之类的东西?并重新考虑我是否在解决这个问题的正确道路上。我认为我需要一个针对我的用例的深入示例,并通过详尽的分步说明来了解更多关于 Promises 以及这个用例场景的信息。

到目前为止,我已经尝试将我的工作代码与这些关于重试承诺的 SO 答案集成,例如:

以及阅读博客文章,例如;

我也看过无数的文档,比如;

最后,一些随机的(结构不太好的)链接;

问题

如何将现有代码转换为上述示例,或者创建一个类似 API 的新方法来处理现有代码(解决我现有的问题)?

标签: javascriptpromise

解决方案


这是您的函数的包装器makeRequest(),可让您传递最大重试次数和重试之间的延迟。它返回一个承诺,如果成功则解决,如果在 maxRetries 之后仍然有错误,则拒绝。

function makeRequestRetry(requestMethod, navToURL, maxRetry = 3, retryDelay = 3000) {
    let retryCnt = 0;

    function delay(t) {
        return new Promise(resolve => {
            setTimeout(resolve, t);
        });
    }
    
    function run() {
        return makeRequest(requestMethod, navToURL).catch(function (err) {
            ++retryCnt;
            if (retryCnt > maxRetry) {
                console.error('Max retries exceeded. There was an error!', err.statusText);
                throw err;
            }
            console.error('Retry #' + retryCnt + ' after error', err.statusText);
            // call ourselves again after a short delay to do the retry
            // add to the promise chain so still linked to the originally returned promise
            return delay(retryDelay).then(run);
        });
        
    }
    return run();
}

而且,有人会这样称呼它:

makeRquestRetry('GET', someURL, 5, 5000).then(result => {
    console.log(result);
}).catch(err => {
    console.log(err);
});

或者,使用重试参数的默认值:

makeRquestRetry('GET', someURL).then(result => {
    console.log(result);
}).catch(err => {
    console.log(err);
});

推荐阅读