【问题标题】:Native JS Promise Retry Wrapper原生 JS Promise 重试包装器
【发布时间】:2020-01-07 13:59:52
【问题描述】:

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

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

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

问题大纲: 由于我基本上正在做的工作流程如下; (based off this SO question/answer)

  1. Promise 对象内发送带有GM_xmlhttpRequest'GET' 请求
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 中返回成功状态时 - 承诺返回 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 返回 JSON 数据,我使用检索到的数据更新当前窗口页面。

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

我的想法

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

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

除了阅读博客文章,还喜欢;

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

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

问题

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

【问题讨论】:

  • 您确实很好地调查了您的问题,并且创建您的问题似乎涉及很多工作,但对我来说,这似乎有点过于宽泛。所以你的问题是如何在连接失败的情况下重试下载?您想从哪个时间点重试?
  • @Icepickle - 是的,这是我的问题。为设置信息道歉,我想彻底地问我的问题。
  • 男孩,我真的不喜欢将普通回调包装到 Promise 中然后使用该 Promise 制作接口的代码,要求调用者使用普通回调。请从您的界面返回您的承诺,并让调用者使用您的承诺。所以....更有用。
  • "我已尝试将我的工作代码与这些 SO 答案集成" - 请向我们展示这些尝试的代码,并描述它们是如何失败的。
  • makeRequest() 返回一个干净的承诺。然后makeRequestWrapper() 接受该承诺并将其转回回调接口。这就是我反对的。如今,您的调用者可以很好地使用 Promise 接口,事实上,它可以比回调接口做更多的事情(这就是发明 Promise 的全部原因)。

标签: javascript promise


【解决方案1】:

这是您的 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);
});

【讨论】:

  • 感谢您的回答,从我测试的 100 多个链接来看,这似乎每次都成功!我还没有完全了解 Promise,但是我认为跟踪工作示例可以帮助我直观地了解调用堆栈中正在发生什么调用,并了解每个调用需要返回什么。干杯
  • @NickBell - 也许你应该找出为什么请求有时会失败,然后重试成功,看看你是否能解决根本问题。您是否在短时间内向一位主机发出过多请求?
猜你喜欢
  • 2023-03-23
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多