如何重复请求,直到一个成功而没有在节点中阻塞?

Luk*_*vat 24 javascript node.js

我有一个带参数和回调的函数.它应该向远程API发出请求并根据参数获取一些信息.当它获得信息时,它需要将其发送到回调.现在,远程API有时无法提供.我需要我的功能继续尝试,直到它设法做到这一点然后用正确的数据调用回调.

目前,我在函数内部有以下代码,但我认为像while(!done); 是不正确的节点代码.

var history = {};
while (true) {
    var done = false;
    var retry = true;
    var req = https.request(options, function(res) {
        var acc = "";
        res.on("data", function(msg) {
            acc += msg.toString("utf-8");
        });
        res.on("end", function() {
            done = true;
            history = JSON.parse(acc);
            if (history.success) {
                retry = false;
            }
        });
    });
    req.end();
    while (!done);
    if (!retry) break;
}
callback(history);
Run Code Online (Sandbox Code Playgroud)

我该怎么做正确的方法?

Dmi*_*eev 37

没有必要重新发明轮子......在这种情况下,您可以使用流行的异步实用程序库,"重试"方法.

// try calling apiMethod 3 times
async.retry(3, apiMethod, function(err, result) {
    // do something with the result
});

// try calling apiMethod 3 times, waiting 200 ms between each retry
async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
    // do something with the result
});
Run Code Online (Sandbox Code Playgroud)

async GitHub页面

async.retry docs

  • @AbimbolaEsuruoso每月下载2600万,他们必须做正确的事情.你不能认真,文档清楚地说明你如何使用该方法,包括代码示例!也许你应该发一个关于它的问题,我很乐意给你一个额外的用法例子. (11认同)
  • 如果轮子是方形的,那我认为我们最好重新发明它。您是否尝试过使用上述库?仅阅读文档,我不知道我应该如何使用async.retry API,因为文档确实提供了任何具体而全面的示例 (2认同)

dc5*_*dc5 24

绝对不是要走的路 - 而(完成); 将进入一个硬循环并占用你所有的CPU.

相反,你可以做这样的事情(未经测试,你可能想要实现某种退避):

function tryUntilSuccess(options, callback) {
    var req = https.request(options, function(res) {
        var acc = "";
        res.on("data", function(msg) {
            acc += msg.toString("utf-8");
        });
        res.on("end", function() {
            var history = JSON.parse(acc);  //<== Protect this if you may not get JSON back
            if (history.success) {
                callback(null, history);
            } else {
                tryUntilSuccess(options, callback);
            }
        });
    });
    req.end();

    req.on('error', function(e) {
        // Decide what to do here
        // if error is recoverable
        //     tryUntilSuccess(options, callback);
        // else
        //     callback(e);
    });
}

// Use the standard callback pattern of err in first param, success in second
tryUntilSuccess(options, function(err, resp) {
    // Your code here...
});
Run Code Online (Sandbox Code Playgroud)

  • 我知道我的循环是一个坏主意,但我对使用这样的递归感觉非常糟糕.如果需要100次尝试才能得到它怎么办?在终于成功之后需要退出的100个电话似乎不是一个好主意.我是节点的新手,所以我可能错了. (2认同)
  • 记住这里中间有一个异步调用。这不是递归调用 - 您不会建立堆栈。是的 - 您可能还想实现一个计数器,以便在放弃之前只重试 'n' 次。 (2认同)
  • 是的,它确实.闭包中的任何变量仍然存在,但函数确实退出.您可以添加一些console.log(...)语句,以更好地了解正在发生的事情. (2认同)

Dyl*_*ogg 7

我发现Dmitry使用异步实用程序库的答案非常有用,也是最好的答案.

这个答案将他的例子扩展到定义apiMethod函数的工作版本并向其传递一个参数.我打算将代码添加为注释,但单独的答案更清晰.

const async = require('async');

const apiMethod = function(uri, callback) {
  try {
    // Call your api here (or whatever thing you want to do) and assign to result.
    const result = ...
    callback(null, result);
  } catch (err) {
    callback(err);
  }
};

const uri = 'http://www.test.com/api';

async.retry(
    { times: 5, interval: 200 },
    function (callback) { return apiMethod(uri, callback) },
    function(err, result) {
      if (err) {
        throw err; // Error still thrown after retrying N times, so rethrow.
      }
  });
Run Code Online (Sandbox Code Playgroud)

重试文档:https://caolan.github.io/async/docs.html#retry

注意,在任务中调用apiMethod(uri,callback)的替代方法是使用async.apply:

async.retry(
        {times: 5, interval: 200},
        async.apply(task, dir),
        function(err, result) {
          if (err) {
            throw err; // Error still thrown after retrying N times, so rethrow.
          }
      });
Run Code Online (Sandbox Code Playgroud)

我希望这为某人提供了一个很好的复制/粘贴锅炉板解决方案.


Joo*_*oon 5

这是你想要做的吗?

var history = {};

function sendRequest(options, callback) {
    var req = https.request(options, function (res) {
        var acc = "";
        res.on("data", function (msg) {
            acc += msg.toString("utf-8");
        });
        res.on("end", function () {
            history = JSON.parse(acc);
            if (history.success) {
                callback(history);
            }
            else {
                sendRequest(options, callback);
            }
        });
    });
    req.end();
}

sendRequest(options, callback);
Run Code Online (Sandbox Code Playgroud)