首先是一个概述。 Node.js 将所有网络都作为非阻塞和异步的。这意味着当您发出网络请求时,它会启动操作并立即返回并继续执行其余代码。稍后会调用与该异步操作相关的回调或承诺。
在您的具体情况下,您的整个 .forEach() 循环运行完成,在其中任何一个完成之前启动其中的所有网络请求。因此,您在任何回调运行之前都在执行console.log(booksArray.length);,因此数组仍然是空的。
任何时候你想在 node.js 中对多个异步操作进行排序或协调,你都需要使用 Promise。它已经内置到语言中已有几年了,它只是一种使用异步操作进行编程的更好的方法。我建议你找到一些关于 Promise 如何工作的好教程并学习它们。
那么,你正在使用的库request() 是旧的并且已经进入维护模式并且不支持承诺。还有各种modern alternatives。我个人使用got() library,这就是我在下面说明的内容。
然后,我不知道你的 parseString() 函数是什么,但它显然不使用承诺并且似乎是异步的,所以为了在基于承诺的工作流程中使用它,我“承诺”它使用 node.js 内置的util.promisify()。如果它有一个 Promise 接口,你应该直接使用它。
不管怎样,这就是我的建议:
const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);
// this function returns a promise
async function getOverview(req, res) {
const books = await Book.find();
const booksArray = await Promise.all(books.map(book => {
const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
let bookData = await got(url);
return ps(bookData);
}));
console.log(booksArray.length);
// make the booksArray be the resolved value of the promise returned
// by this async function
return booksArray;
}
这会使您的所有网络调用并行进行,然后使用Promise.all() 指示它们何时全部完成并按顺序收集所有结果。
如果由于某种原因,您不能并行发出所有这些请求(例如目标服务器对象太多),那么您也可以像这样一次运行它们:
const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);
// this function returns a promise
async function getOverview(req, res) {
const books = await Book.find();
const booksArray = [];
for (let book of books) {
const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
let bookData = await got(url);
let data = await ps(bookData);
booksArray.push(data);
}
console.log(booksArray.length);
// make the booksArray be the resolved value of the promise returned
// by this async function
return booksArray;
}
而且,由于这些是async 函数,它们返回一个promise,因此调用者必须使用await 或.then() 从promise 中获取值。
getOverview(...).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
如果您确实打算将 req 和 res 传递到该函数并在那里使用它们,那么您可能也需要本地错误处理,因此如果您的任何 await 操作被拒绝,您可以捕获该错误并发送错误响应。您可以在函数体内使用 try/catch 来执行此操作。您可能只使用一个顶级try/catch,然后在catch 处理程序中发送错误响应。