【问题标题】:How to make subsequent http requests asynchronously inside a while loop without using promises and async/await?如何在不使用 Promise 和 async/await 的情况下在 while 循环内异步发出后续 http 请求?
【发布时间】:2019-11-23 19:17:40
【问题描述】:

我有一个用 Node 编写的服务,并将获取特定城市团队列表的请求发送到另一个 API 服务。

请求参数:

  • city
  • page
  • per_page

响应正文:

  • page
  • per_page
  • more
  • teams

per_page 默认设置为 1000,但团队数量远大于 1000,并且响应中未返回某些团队。我需要所有的团队。 由于某些原因我不打算在这里指定,我不想更改per_page参数,但我想做的是发送第一个请求并检查响应正文中的more是否为true(意思是还有更多的团队),我从响应正文中提取团队并将其添加到最终结果中,增加page 参数并使用参数发送请求:

{
  "page": "2",
  "city": "chicago",
  "per_page": 1000, 
 }

我一直这样做,直到 more 标头为 false

该服务是用 Node 编写的,我使用 request 包来发送 HTTP 请求。最初,我想使用while loop

function teamsInCity(city, page, result, callback) {
 let more = true;

  while(more) {
  // send the request
  // extract teams into the result array
  // check if there is more
  // send another request
  // keep sending requests and appending teams until more is false
  } 
  // return the final result with all the teams
}

但这不起作用,因为while loop 发送请求并继续执行下一行代码而不等待响应。所以我用递归解决了它并且它有效。但我需要对尾调用进行优化,我不确定,但我认为 Node 不支持,但我可能错了。

有没有办法在while loop 内部发送请求而不使用async/await 和承诺?

下面是我的递归工作函数:

function teamsInCity(city, page, result, callback) {
  request.get({
    url: 'my_service' + '/teams',
    qs: { 'city' : city, 'page': page, 'per_page': 1000 },
    json: true
  }, function(err, response, body) {
    if (err) { return callback(err); }

    result = result.concat(body.teams);

    if (body.more) {
      page++;
      return teamsInCity(city, page, result, callback);
    }
    return callback(null, result);
  });
}

这里是调用teamsInCity()的路由

router.get('/:city', function(req, res) {
  var data = {teams: []};
  myService.teamsInCity(req.params.city, 1, [], function(err, teams) {
    if (err) {
      logger.error('Error while retrieving teams in ' + req.params.city);
    }
    data.teams = _.sortBy(teams, 'name');
    res.send(data);
  });
});

谢谢!

【问题讨论】:

  • " 不使用 async/await 和 promises?" ????有什么理由不?
  • "但我需要对它进行尾调用优化" 你的函数已经对尾调用进行了优化
  • @AvcS 您能否详细说明您的评论?根据this post V8 没有实现 TCO。

标签: javascript node.js api recursion request


【解决方案1】:

您可以使用async 库和whilst 函数。我不认为这会让你的 Stack Overflow ;-)

例如:

const async = require("async");

function teamsInCity(city, callback) {
    let result = [];
    let page = 0;
    let more = true;
    async.whilst(
        () => more,
        (callback2) => {
            request.get({
                url: 'my_service' + '/teams',
                qs: { 'city' : city, 'page': page++, 'per_page': 1000 },
                json: true
            }, (err, response, body) => {
                if (err) { 
                    callback2(err); 
                    console.error(`teamsInCity: Error occurred: ${err.message}`);
                } else {
                    console.log(`teamsInCity: Page#: ${page}, Team count: ${body.teams.length}, More pages: ${body.more}`);
                    more = body.more;
                    result = result.concat(body.teams);
                    callback2(null, result);
                }
            })
        },
        (err, result) => {
            if (err) console.error("Final callback error: ", err.message);
            if (!err) console.log("Final callback result: ", result);
            callback(err, result);
        }
    );
}

【讨论】:

  • 感谢您的解决方案。它解决了 TCO 问题,但没有做我需要做的事情。如果我将per_page 更改为 10,它将获取包含 10 个团队的第一页并返回给调用者。换句话说,它不会迭代。它不会发送请求以获取剩余的 990。我发送请求的服务为所有团队向 db 发出一个请求。它可以超过1000个团队。所以我想向服务发出多个请求,并将per_page 设置为 1000,直到我获取所有团队。
  • 嘿@buterfly85,感谢您测试答案!我想知道为什么它不继续调用.. 通常这意味着 body.more 属性是错误的.. 你能添加我更新答案的日志调用吗?我认为这应该比较容易解决!
  • 日志是Final callback result: undefined。此外,我通过添加使用此函数作为处理程序的路由器来更新我的帖子。我认为async.whilst 中的第二个参数应该是() => {} 而不是callback => {}。我试过了,它返回了团队,但它只提出了一个请求。它不会继续迭代并发送后续请求。
  • 啊,是的,这很令人困惑,但实际上处理函数中的(回调)变量与根变量的范围不同。也许我会给它一个不同的名字。我仍然很困惑为什么没有将 body.more 变量分配给 more 变量!
  • 没关系。出于某种原因,正在安装的async 版本是 1.5.0。我更新了版本,它工作。所以非常感谢!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2020-08-03
  • 2016-12-30
  • 2020-02-18
  • 2022-08-06
  • 2018-01-22
  • 2021-11-07
相关资源
最近更新 更多