0

I want to read columns from different CSV files, and combine those columns into an array. I'm using fs.readFile to read the CSV files and a callback which processes the data and pushed a new element onto the array of columns. This array of columns is then sent on to another part of the software (I'm building an electron app, so it's sent to the render process).

The issue I'm having is that 'fs.readFile' is asynchronous, so my columns array is sent off before any of the fs.readFile calls finish, resulting in an empty array.

What's the best way to solve this issue? It is to simply use fs.readFileSync? Is there a way to do it without blocking execution?

Minimal example code below:

//Process each column, reading the file and extracting the data one at a time
let columns: (number[] | undefined)[] = []; //Somewhere to store the processed columns
for (const dataHandle of dataHandles)
{
  //read the file as a raw string
  fs.readFile(dataHandle.filePath, (error: any, data: any) => {
    if (error) {
      console.log("Error reading file:", error);
    } else {
      data = data.toString();
      const newColumn = parseColumnFromStringDataframe(data, dataHandle.columnName);
      columns.push(newColumn);
    }
  })
}
//Finished processing each column, so send response.
//BUT... readfile is non-blocking! Sends the response before anything is pushed to columns! How can we wait in a smart way?
console.log(columns); // []
mainWindow?.webContents.send("readDataProductColumnsResponse", columns); //Sends response
Verwirrt
  • 403
  • 2
  • 13

2 Answers2

2

This was answered here: https://stackoverflow.com/a/34642827/7603434

Basically you have to create an array of promises and then call Promise.all(promises);

const fs = require("fs");
const files = ["app.js", "index.html", "script.js"];

const readAllFiles = async () => {
  let promises = [];
  for (const f of files) {
    promises.push(fs.promises.readFile(f, "utf8"));
  }
  return Promise.all(promises);
};

async function run() {
  readAllFiles()
    .then((fileContents) => {
      console.log("done", fileContents);
      // fileContents is an array that contains all the file contents as strings
    })
    .catch((err) => {
      console.error(err);
    });
}

run();
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 09 '22 at 16:49
1

The more compact solution would be

const fs = require("fs").promises;
const files = ["aaa.txt", "bbb.txt"];

(async () => {
  Promise.all(files.map((file) => fs.readFile(file, "utf8")))
    .then((data) => {
      console.log(data);    // ['content of aaa', 'content of bbb']
    })
    .catch((err) => {
      console.error(err);
    });
})();
RAllen
  • 1,235
  • 1
  • 7
  • 10