我在使用Node.js的Promise时遇到问题。我正在使用cheerio和request-promise进行Web抓取,并且我想在使用Promise.all(promises).then(...)
语法执行了所有异步函数后,将结果写入CSV文件。它运行正常,但突然程序完成,没有任何错误或被拒绝,但没有执行then(...)
部分(没有文件,也没有日志)。这是我的代码:
const rp = require('request-promise');
const cheerio = require('cheerio');
const csv = require('fast-csv');
const fs = require('fs');
var results = [];
var promises = [];
function getResults(district, branch) {
for (let i = 65; i <= 90; i++) {
let letter = String.fromCharCode(i);
let generalOptions = {
uri: 'https://ocean.ac-lille.fr/publinet/resultats?previousValCri1=' + branch + '&previousValCri0=0' + district + '&previousIdBaseSession=pub_24&actionId=6&valCriLettre=' + letter,
transform: function (body) {
return cheerio.load(body);
}
};
promises.push(new Promise(function(resolve, reject) {
rp(generalOptions)
.then(($) => {
$('.tableauResultat').find('tr').each(function(i, elem) {
let children = $(elem).children('td');
let name = $(children[0]).text();
results.push({name: name, result: 42, branch: branch});
resolve();
});
})
.catch((err) => {
console.log(err);
//reject();
});
}
));
}
}
getResults(process.argv[2], process.argv[3]);
Promise.all(promises).then(() => {
console.log("Finished!");
var ws = fs.createWriteStream('results_bis.csv', {flag: 'w'});
csv.write(results).pipe(ws);
}).catch((err) => {
console.log(err);
});
答案 0 :(得分:1)
results
一起使用时, Promise.all
数组通常是反模式。预期传递给Promise.all
的诺言会返回必要的结果,因此可以以Promise.all(promises).then(results => { ... })
的形式访问它们。
不需要基于回调的处理$('.tableauResultat').find('tr').each(...)
,这会导致控制流不佳。由于Cheerio提供了类似jQuery的API,因此可以将结果转换为数组并以原始JavaScript特有的方式进行处理。
上面的代码使用promise构造反模式。如果有一个new Promise
,则不需要rp(generalOptions)
。它导致了问题;没有计划执行的时间时,Node.js存在。 promise
中的某些promise正在等待执行,因为resolve
和reject
都没有被调用,所以它们没有机会被解决。如果从未触发each
回调,则会发生这种情况。可以通过将resolve()
移到each
回调之外来解决此问题。
一种更直接的方法,可以为模糊的问题留出更少的空间,并且更易于调试:
const promises = [];
function getResults(district, branch) {
for (let i = 65; i <= 90; i++) {
...
promises.push(
rp(generalOptions)
.then(($) => {
const trs = $('.tableauResultat').find('tr').toArray();
const results = trs.map(elem => {
let children = $(elem).children('td');
let name = $(children[0]).text();
return {name, result: 42, branch};
});
return results;
})
.catch((err) => {
console.log(err);
return null; // can be filtered out in Promise.all results
// or rethrow an error
})
);
}
}
getResults(...);
Promise.all(promises).then(nestedResults => {
const results = nestedResults.reduce((flatArr, arr) => flatArr.concat(arr), []);
console.log("Finished!");
var ws = fs.createWriteStream('results_bis.csv', {flag: 'w'});
csv.write(results).pipe(ws);
}).catch((err) => {
console.log(err);
});
请注意,当前未处理来自文件流的错误。即使有问题,也不会触发Promise.all
catch
。