0

Trying to return a list of disk folder, then for each folder get the files in it. I want the result set to be a single array of objects. The final output from the function call to getPosts() is a promise with an empty array.

Promise { [] }
  var accountId = req.body.accountId;

  var accountFilePath = 'accountFiles/' + accountId;

  function getPosts(accountFilePath) {
    return new Promise((resolve, reject) => {
      let postArray = [];
      fs.readdir(accountFilePath, (err, posts) => {
        for (let i = 0; i < posts.length; i++) {
          var postId = posts[i];
          var postFilePath = accountFilePath + '/' + postId;
          var postObject = getFiles(postId, postFilePath)
          postObject.then((response) => {
            postArray.push(response)
          })
        }
      });
      resolve(postArray)
    })
  }

  function getFiles(postId, postFilePath) {
    return new Promise((resolve, reject) => {
      fs.readdir(postFilePath, function (err, files) {
        postObject = {
          [postId]: files
        }
        resolve(postObject)
      });
    })
  }

  var fullPostArray = getPosts(accountFilePath)
  console.log(fullPostArray);

Desired Result:

[
  {
    'folder1': [
      'DSC0366.jpg',
      'DSC8874.jpg',
      'DSC8878.jpg',
      'DSC8951.jpg'
    ]
  },
  {
    folder2: [
      'nikki.jpg',
      'richard.jpg',
      'billy.jpg'
    ]
  }
]

1 Answers1

0

Promise were invented to tackle the so called callback hell: Multiple asynchronous callbacks firing at different times, making it hard to keep things in order. While you tried to utilize promises you ised them at the wrong level: You still have various asynchronous things going on inside the promise, and that makes things complicated (and they go wrong). Instead, try to wrap the smallest asynchronous tasks into promises, in your case that would be (1) reading a folder, (2) getFiles (which you already did):

  function readFiles(path) {
    return new Promise((resolve, reject) => {
     fs.readdir(path, (err, files) => {
       if(err) reject(err) else resolve(files);
     });
   });
 }

Now getPosts can be written as a chain of promising tasks, and you can use Promise.all to turn an array of promises into one promise:

  function getPosts(accountFilePath) {
    return readFiles(accountFilePath).then(posts => { // ¹
     return Promise.all(posts.map(postId => { // ²
       var postFilePath = accountFilePath + '/' + postId;
       return getFiles(postId, postFilePath); // ³
    }));
   });      
  }

¹: First the files are read, and then the next task is done. If you return a Promise from a .then callback (like in this case) that promise will also be chained.

²: Every post can then be mapped to a Promise (³) that resolves to the desired result, by using Promise.all on that the promise chain results in an array of results.


The newer async / await syntax makes that way easier (although the logic stays exactly the same):

 async function getPosts(accountFilePath) {
  const posts = await readFiles(accountFilePath);
  const result = await Promise.all(posts.map(postId => {
    const postFilePath = accountFilePath + '/' + postId;
    return getFiles(postId, postFilePath);
  }));
  return result;
}    
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151