-2

Please, I know this question has been answered before. I have read this answer & this article, but I can't figure out how to fix my code yet.

I have created a function that reads some file's content & returns a new Promise. Here it's the function:

// array of string representing each file's path
const allSpecFiles = [
  '/path/to/the/file1.spec.js',
  '/path/to/the/file2.spec.js',
  '/path/to/the/file3.spec.js'
];

// method to read an individual file content & return a Promise
const readFileContent = file => {
  return new Promise((resolve, reject) => {
    fs.readFile(file, 'utf8', (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    });
  });
};

Now, I'm trying to loop through an array of strings storing each file's path, call the readFileContent() method & pass current loop's value as its param with map() method since I would like to create another array of strings with each file's content.

This is what I have tried:

const allSpecFilesArr = allSpecFiles.map(async file => await readFileContent(file));
console.log(allSpecFilesArr); // I get Promise { <pending> }

I have also tried wrapping the whole script like so:

(async () => {
  const allSpecFilesArr = await allSpecFiles.map(file => readFileContent(file));
  console.log(allSpecFilesArr); // still prints Promise { <pending> }
})();

What I'm doing wrong?

Manuel Abascal
  • 5,616
  • 5
  • 35
  • 68

3 Answers3

1

There's no need to wrap fs.readFile, use fs/promises. Try this out:

const fs = require('fs/promises')
const paths = [ './one', './two' ]
;(async () => {
  const contents = await Promise.all(paths.map((p) => fs.readFile(p, 'utf8')))
  console.log(contents)
})()
Zac Anger
  • 6,983
  • 2
  • 15
  • 42
1

The second solution is partially correct. You're awaiting the result of the map function which is, in this case, an array of promises.

If you removed await in front of the map call and called await Promise.all(allSpecFilesArr) you will get what you need.

You Could do something like this:

async read (paths) {
 const promises = [];
 for (path in paths) {
   promises.push(readFileContent(path));
 }

 
 const arrOfContentYouWant = await Promise.all(promises);
 return arrOfContentYouWant;
}
Mu-Majid
  • 851
  • 1
  • 9
  • 16
  • 1
    That's not correct, you can pass an async function to `map`, you just have to expect that you're going to get an array of Promises back. You can test this easily with something like `Promise.all([1,2,3].map(async (x) => x)).then(console.log)`. Also there is no `all` method on an array of promises, I think you meant `Promise.all`. – Zac Anger Jan 14 '21 at 19:59
  • @ZacAnger, Thanks for pointing out the typo, I edited it. For the map part, You're totally right, what I meant is without Promise.all we can not use async function with functions like map because synchronous code won't sit and wait for your asynchronous code to resolve, instead it will the fire the function and move on. I will remove this paragraph to avoid any confusion. For more info about using map with promises https://stackoverflow.com/questions/55225272/map-function-with-async-await – Mu-Majid Jan 14 '21 at 20:12
0

What you want is to use Promise.all(allSpecFilesArr) because that is an array of promises. You can await it and you will receive an array of data returned from the inner resolved promises.

const fileContents = await Promise.all(allSpecFilesArr);
Terry
  • 63,248
  • 15
  • 96
  • 118