Home > Mobile >  Chained Promises not fully resolving on await
Chained Promises not fully resolving on await

Time:11-24

I have a function that reads files in a directory asynchronously (readdir) and filters for csv files. I also have an async function that calls readdir filtered for csv files and then iterates through them with fast-csv. Logging to the console the list and its length within the .on('end') function, I can see that they produce the desired results. however, my async call only resolves the first iteration.

const fs = require(`fs`);
const path = require(`path`);
const csv = require(`fast-csv`);
var ofsActivities = [];

const currDir = path.join(__dirname   `/../Downloads/`);

const readdir = async dirname => {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (error, filenames) => {
        error ? reject(error) : resolve(filenames);
      });
  });
};

const filtercsvFiles = (filename) => {
  return filename.split(`.`)[1] == `csv`;
};

const ofsDataObjectArray = async () => {
  return readdir(currDir).then(async filenames => {
    return await new Promise((resolve, reject) => {
      filenames = filenames.filter(filtercsvFiles);
      for (let i = 0; i < filenames.length; i  ) {
        let currFilePath = currDir   filenames[i];
        console.log(`Reading File: ${filenames[i]}`);
        csv
          .parseFile(currFilePath)
          .on(`data`, (data) => {
            //Doing stuff
          })
          .on(`error`, error => reject(error))
          .on(`end`, () => resolve(ofsActivities)); //Inserting a console.log(ofsActivities.length) logs the correct and expected length on the last iteration
      }
    });
  });
};

(async () => {
  let list = await ofsDataObjectArray(); // This seems to only resolve the first iteration within the promise
  console.log(list.length);
})();

CodePudding user response:

You need to call resolve() only when the LAST csv.parseFile() is done. You're calling it when the FIRST one is done, thus the promise doesn't wait for all the others to complete. I'd suggest you promisify csv.parseFile() by itself and then await that inside the loop or accumulate all the promises from csv.parseFile() and use Promise.all() with all of them.

Here's using await on each csv.parseFile():

const ofsDataObjectArray = async () => {
    return readdir(currDir).then(async filenames => {
        filenames = filenames.filter(filtercsvFiles);
        for (let i = 0; i < filenames.length; i  ) {
            let currFilePath = currDir   filenames[i];
            console.log(`Reading File: ${filenames[i]}`);
            await new Promise((resolve, reject) => {
                csv.parseFile(currFilePath)
                    .on(`data`, (data) => {
                        //Doing stuff
                    })
                    .on(`error`, reject)
                    .on(`end`, () => resolve(ofsActivities)); 
            });
        }
    });
};

Or, here's running them in parallel with Promise.all():

const ofsDataObjectArray = async () => {
    return readdir(currDir).then(filenames => {
        filenames = filenames.filter(filtercsvFiles);
        return Promise.all(filenames.map(file => {
            let currFilePath = currDir   file;
            console.log(`Reading File: ${file}`);
            return new Promise((resolve, reject) => {
                csv.parseFile(currFilePath)
                    .on(`data`, (data) => {
                        //Doing stuff
                    })
                    .on(`error`, error => reject(error))
                    .on(`end`, () => resolve(ofsActivities)); 
            });

        }))
    });
};
  • Related