Обещание - PullRequest
       4

Обещание

2 голосов
/ 05 апреля 2020

Я управляю полусложной цепочкой обещаний с foreach l oop в середине. Проблема, с которой я сталкиваюсь, заключается в том, что финальный .then() выполняется до того, как завершится foreach l oop, в результате чего получается совершенно пустой массив 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);
    });

Есть ли способ ждать что должен завершить l oop, прежде чем перейти к следующему .then()? Выпуск напрямую на foreach и окончательный console.log(dailyTotals);

Ответы [ 3 ]

2 голосов
/ 05 апреля 2020

Нет. Вы не получите свое обещание дождаться .forEach до возвращения. Если ваша .forEach функция включает в себя асинхронный код, вы не должны использовать это. Вместо этого вы можете go с немного разными подходами:

  1. Использовать традиционный для -l oop, поскольку он блокирует.
  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/

2 голосов
/ 05 апреля 2020

Прежде всего, я должен отметить, что существуют альтернативные методы для Array#forEach:

  • Array#map, который предназначен для создания нового массива путем сопоставления исходного (того же, что и вы делаете с .forEach) и
  • Array#filter (его название говорит само за себя).

Проблема в том, что у вас есть цепочка обещаний и основанная на обратном вызове модуль. Чтобы смешать их, используйте конструктор Promise и используйте Promise.all, чтобы объединить их в одно обещание:

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);
    });
0 голосов
/ 05 апреля 2020

Похоже, у вашего второго then есть асинхронный c код, papa.parse(), поэтому ваш запрос для каждого во втором then будет выполняться синхронно с пустыми значениями, и, следовательно, финальный then будет выполняться с этими пустые значения. Вы должны возвращаться из второго блока then только тогда, когда ваш асин c код завершает

Вам нужно будет разрешать обещания в обратных вызовах complete и использовать подобные Promise.all, чтобы определить, когда все complete обратные вызовы выполняются

1) измените их для каждого на карту ..

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

2) верните обещание из вашего обратного вызова карты.

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

3) возобновить свое обещание из complete обратного вызова

reolve()

4) использовать return Promise.all(arrayPromises)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...