如何在 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
而不是计数。
但真正的问题是,为什么 arrTenures
和 arrNames
是分开的数组?它们的信息显然彼此相关,因此依靠数组索引来耦合它们似乎是个坏主意。相反,您应该尝试保留一个包含所有相关信息的对象数组。例如:
[
{ 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);
});
这有几个优点:
Promise.all()
为您整理结果。
rp()
为您检查 2xx 状态。
- 这样可以chained/sequenced配合其他异步操作更容易。
- 错误处理更简单,低级错误会自动传播到顶层。
- 这是 throw-safe(如果抛出异步错误,它们将被捕获并传播)。
我一直在使用请求迭代几个 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
而不是计数。
但真正的问题是,为什么 arrTenures
和 arrNames
是分开的数组?它们的信息显然彼此相关,因此依靠数组索引来耦合它们似乎是个坏主意。相反,您应该尝试保留一个包含所有相关信息的对象数组。例如:
[
{ 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);
});
这有几个优点:
Promise.all()
为您整理结果。rp()
为您检查 2xx 状态。- 这样可以chained/sequenced配合其他异步操作更容易。
- 错误处理更简单,低级错误会自动传播到顶层。
- 这是 throw-safe(如果抛出异步错误,它们将被捕获并传播)。