【问题标题】:How do I use the Request function in node.js within a for loop and have it return output in correct order?如何在 for 循环中使用 node.js 中的 Request 函数并让它以正确的顺序返回输出?
【发布时间】:2018-04-08 23:25:42
【问题描述】:

我一直在使用 request 遍历多个 XML 条目,并使用cheerio.js 将每篇文章、日期和网址返回到控制台。主要问题是每次输出都会以不同的顺序出现,因为 Request 是一个异步函数。一般来说,我对 javascript 真的很缺乏经验,并且想知道如何检索一致的输出(我一直在阅读有关异步和承诺的内容,我只是不确定如何实现它们)。 这是我的代码:

var count = 0;
for(var j = 0; j < arrNames.length; j++){
  request('http://dblp.org/search/publ/api?q=' + arrNames[j], function(error, response, html){
    if (!error && response.statusCode == 200){
      var $ = cheerio.load(html, {xmlMode: true});
      console.log($('query').text()+'\n');

      $('title').each(function(i, element){
        var title = $('title').eq(i).text();
        var year = Number($('year').eq(i).text());
        var url = $('ee').eq(i).text();

        if (year >= arrTenures[count]){
          console.log(title);
          console.log(year);
          console.log(url + '\n');
        }
      });

      count++;
    }
  });
}

【问题讨论】:

  • 如果你使用 Promise,它会是这样的 Promise.all( arrNames.map( name =&gt; request("http://dblp.org/search/publ/api?q=" + name) ).then( arrayWithResult =&gt; { /* do your thing here*/ })

标签: javascript node.js request cheerio


【解决方案1】:

虽然您已经找到了解决方案,但我想我会向您展示如何使用 ES6 Promise(管理多个异步操作的更现代的方法)来做到这一点:

const rp = require('request-promise');

Promise.all(arrNames.map(item => {
    return rp('http://dblp.org/search/publ/api?q=' + item).then(html => {
        const $ = cheerio.load(html, {xmlMode: true});

        return $('title').map(function(i, element){
            const title = $(element).text();
            const year = Number($('year').eq(i).text());
            const url = $('ee').eq(i).text();
            return {title, year, url};
        }).get();
    });
})).then(results => {
    // results is an array of arrays, in order
    console.log(results);
}).catch(err => {
    console.log(err);
});

这有几个优点:

  1. Promise.all() 为您整理结果。
  2. rp() 为您检查 2xx 状态。
  3. 这可以更轻松地与其他异步操作链接/排序。
  4. 错误处理更简单,低级错误会自动为您传播到顶层。
  5. 这是抛出安全的(如果抛出异步错误,它们会被捕获并传播)。

【讨论】:

  • 这是一个非常有用的答案,让所有东西都按正确的顺序返回确实非常好。这使得 promise 看起来比我想象的要容易得多,很难从不相关的例子中收集到。谢谢!
【解决方案2】:

您似乎正在尝试捕获每个请求的迭代次数,因此请使用forEach 并利用它的第二个参数,该参数表示迭代索引:

arrNames.forEach((q, requestIndex) => {
  request('http://dblp.org/search/publ/api?q=' + q, (error, response, html) => {
    if (error || response.statusCode == 200) return;
    var $ = cheerio.load(html, {
      xmlMode: true
    });
    console.log($('query').text() + '\n');
    $('title').each(function(i, element) {
      var title = $('title').eq(i).text();
      var year = Number($('year').eq(i).text());
      var url = $('ee').eq(i).text();
      if (year >= arrTenures[requestIndex]) {
        console.log(title);
        console.log(year);
        console.log(url + '\n');
      }
    });
  });
});

附带说明一下,一致的缩进确实提高了代码的可读性 - 您可以考虑使用 linter。

【讨论】:

  • 非常感谢您的快速回复!你在我试图捕获每个请求的迭代次数时是非常正确的,而这正是修复它的事情!而且我的缩进在现实生活中稍微好一点,这是我第一次在 SO 上发帖,所以我搞砸了格式。谢谢!
【解决方案3】:

在您第一次尝试时,您可能已经尝试过:

if (year >= arrTenures[j]) {

但注意到这不起作用。这是因为范围界定问题

您可以通过使用像 forEach() 这样的迭代器来解决您的问题,或者只是将您的 for 循环更改为使用 let

for(let j = 0; j < arrNames.length; j++){

现在您可以在支票中使用j 而不是计数。


但真正的问题是,为什么 arrTenuresarrNames 是不同的数组?它们的信息显然相互关联,因此依靠数组索引来耦合它们似乎是个坏主意。相反,您应该尝试保留一个包含所有相关信息的对象数组。例如:

[
  { name: 'some name', tenures: 2 },
  { name: 'another', tenures: 5 }
]

【讨论】:

  • 呵呵,我不知道你可以用 Javascript 中的数组来做到这一点,谢谢你的想法!
  • 然后你可以做类似item.nameitem.tenures :)
  • 哈我只是在谷歌上搜索如何访问这些元素!谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-09
  • 2018-01-11
  • 1970-01-01
  • 2019-09-29
  • 2016-08-16
相关资源
最近更新 更多