如何在 for 循环中使用 node.js 中的 Request 函数并使其 return 以正确的顺序输出?

How do I use the Request function in node.js within a for loop and have it return output in correct order?

我一直在使用请求迭代几个 XML 条目和 return 每篇文章、日期和 url 通过使用 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++;
    }
  });
}

您似乎在尝试捕获每个请求的迭代次数,因此请使用 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。

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

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 }
]

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

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. 这样可以chained/sequenced配合其他异步操作更容易。
  4. 错误处理更简单,低级错误会自动传播到顶层。
  5. 这是 throw-safe(如果抛出异步错误,它们将被捕获并传播)。