繁体   English   中英

Promise.then 在previous.then 之前被解决,foreach 完成

[英]Promise.then being resolved before previous .then with foreach completes

我正在运行一个半复杂的 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);

不,您不会让您的 promise 在返回之前等待.forEach 如果您的.forEach function 包含异步代码,则不应使用它。 相反,您可以使用稍微不同的方法 go :

  1. 使用传统的 for 循环,因为它是阻塞的。
  2. 创建一个范围承诺.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/

首先,我不得不提到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);
    });

看起来你的第二个then有异步代码papa.parse()所以你的 for each 在第二个then将使用空值同步完成,因此最后一个then将使用这些空值执行。 您只需要从第二个返回then在异步代码完成时阻塞

您必须在complete回调中解决承诺,并使用Promise.all之类的方法来检测何时执行所有complete回调

1)将您的每个更改为 map..

const arrayPromises= files.map(... Etc)

2) 从您的 map 回调中返回 promise。

files.map((file) => new Promise((resolve, reject) => {... Everything from your current foraeach}))

3)从complete的回调中旋转您的 promise

reolve()

4) 使用return Promise.all(arrayPromises)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM