我正在运行一个半复杂的Promise链,中间有一个foreach循环。我遇到的问题是,最后的.then()
在foreach循环完成之前就被命中,从而导致dailyTotals
数组完全为空。
fs.readdir(csvPath)
.then(files => {
// Define storage array
var csvFiles = [];
// Loop through and remove non-csv
files.forEach(file => {
if (file !== "README.md" && file !== ".gitignore") {
csvFiles.push(file);
}
});
return csvFiles;
})
.then(files => {
var dailyTotals = [];
files.forEach(filename => {
const loadedFile = fs.createReadStream(csvPath + filename);
var totalCases = 0;
var totalDeaths = 0;
var totalRecovered = 0;
papa.parse(loadedFile, {
header: true,
worker: true,
step: r => {
totalCases += parseIntegerValue(r.data.Confirmed);
totalDeaths += parseIntegerValue(r.data.Deaths);
totalRecovered += parseIntegerValue(r.data.Recovered);
},
complete: () => {
var dailyTotal = {
date: filename.replace(".csv", ""),
parsed: {
confirmed: totalCases,
deaths: totalDeaths,
recovered: totalRecovered
}
};
dailyTotals.push(dailyTotal);
}
});
});
return dailyTotals;
})
.then(dailyTotals => {
console.log(dailyTotals);
});
在解决下一个.then()
之前,有没有办法等待该foreach循环完成?问题直接在foreach和最终console.log(dailyTotals);
答案 0 :(得分:2)
不。您将无法保证等到.forEach
之后再返回。如果您的.forEach
函数包含异步代码,则不应使用该代码。
相反,您可以采用略有不同的方法:
.map
,类似于您对。forEach
所做的操作,我建议这样做:const promises = [array with your values].map(() => {
// If you are running in a node env, you can also make the function async instead of returning a promise
return new Promise((resolve, reject) => {
// Do what you need to
if (error) {
reject(error);
}
resolve(<insert the value that should be returned here>);
});
});
Promise.all(promises).then(() => console.log('all promises done'));
使用一些简单的console.log来查看其工作示例。日志:https://jsfiddle.net/3ghmsaqj/
答案 1 :(得分:2)
首先,我不得不提到Array#forEach
之外还有其他方法:
Array#map
,其目的是通过映射原始数组(与对.forEach
的操作相同)来创建新数组,并且Array#filter
(它的名字不言而喻)。问题是,您有一个Promise链和一个基于回调的模块。要混合使用它们,请使用Promise
构造函数并使用Promise.all
来将它们组合为一个promise:
fs.readdir(csvPath)
.then(files => {
// Filter out non-csv
return files.filter(file => (file !== "README.md" && file !== ".gitignore"))
})
.then(files => {
//Returning a Promise from then handler will be awaited
return Promise.all(files.map(filename => {
const loadedFile = fs.createReadStream(csvPath + filename);
return new Promise(resolve => {
var totalCases = 0;
var totalDeaths = 0;
var totalRecovered = 0;
papa.parse(loadedFile, {
header: true,
worker: true,
step: r => {
totalCases += parseIntegerValue(r.data.Confirmed);
totalDeaths += parseIntegerValue(r.data.Deaths);
totalRecovered += parseIntegerValue(r.data.Recovered);
},
complete: () => {
resolve( {
date: filename.replace(".csv", ""),
parsed: {
confirmed: totalCases,
deaths: totalDeaths,
recovered: totalRecovered
}
});
}
});
});
}));
})
.then(dailyTotals => {
console.log(dailyTotals);
});
您甚至可以省略第一个.then
处理程序,因为.filter
是同步操作,并且可以在单个.then
内完成所有操作:
fs.readdir(csvPath)
.then(files => {
//Returning a Promise from then handler will be awaited
return Promise.all(
files
//Filter here
.filter(file => (file !== "README.md" && file !== ".gitignore"))
//And map without a second then
.map(filename => {
const loadedFile = fs.createReadStream(csvPath + filename);
return new Promise(resolve => {
var totalCases = 0;
var totalDeaths = 0;
var totalRecovered = 0;
papa.parse(loadedFile, {
header: true,
worker: true,
step: r => {
totalCases += parseIntegerValue(r.data.Confirmed);
totalDeaths += parseIntegerValue(r.data.Deaths);
totalRecovered += parseIntegerValue(r.data.Recovered);
},
complete: () => {
resolve( {
date: filename.replace(".csv", ""),
parsed: {
confirmed: totalCases,
deaths: totalDeaths,
recovered: totalRecovered
}
});
}
});
});
})
);
})
.then(dailyTotals => {
console.log(dailyTotals);
});
答案 2 :(得分:0)
您的第二个display-style
具有异步代码then
,因此您的第二个papa.parse()
中的每个代码将与空值同步完成,因此最后一个then
将使用这些空值执行。您只需在异步代码完成后从第二个then
块返回
您将不得不在then
回调中解析promise,并使用complete
之类来检测何时执行所有Promise.all
回调。
1)将每个地图更改为一张地图。
complete
2)从地图回调中返回一个承诺。
const arrayPromises= files.map(... Etc)
3)从files.map((file) => new Promise((resolve, reject) => {... Everything from your current foraeach}))
回调中兑现您的诺言
complete
4)使用reolve()