简体   繁体   中英

Node Promise return object with value of resolved promise

I have some code below that will return all files in a directory with certain keys and values. One of those keys I want to be isDirectory with a boolean value.

The code below works just fine, but I was wondering if there was a way to remove the Promise.all/iterating over promises and instead pushing the resolved value of the stat.isDirectory() to my files objects directly in my map.

My solution I tried but failed:

I tried something like this:

isDirectory: fs.statAsync(path).then((stat) => stat.isDirectory())

And then do a Promise.all on all the isDirectory keys

Working Code:

const Promise = require('bluebird'),
    os = require('os'),
    _ = require('lodash'),
    fs = Promise.promisifyAll(require('fs')),
    desktopPath = `${os.homedir()}/Desktop`;

let files = [];

return fs.readdirAsync(desktopPath)
    .then((filesName) => files = _.map(filesName, (fileName) => ({
        path: `${desktopPath}/${fileName}`,
        name: fileName,
        ext: _.last(fileName.split('.'))
    })))
    .then(() => Promise.all(_.map(files, (file) => fs.statAsync(file.path))))
    .then((stats) => _.forEach(stats, (stat, idx) => {
        files[idx].isDirectory = stat.isDirectory();
    }))
    .then(() => {
        console.log(files);
    })

Is there anyway to remove the Promise.all and _.forEach part at the end? And instead do those actions in my map?

You cannot remove the Promise.all entirely, because you want to wait for all files to finish before you use the final result. But you can do it all in one .then() call.

Because map is synchronous, it won't wait for the fs.statAsync to finish. But you can create an array of promises of fs.statAsync that resolve with the final files object and the simply wait for all of them to complete by using Promise.all .

A verbose version with some comments for clarification:

fs.readdirAsync(desktopPath)
  .then(fileNames => {
    // Array of promises for fs.statAsync
    const statPromises = fileNames.map(fileName => {
      const path = `${desktopPath}/${fileName}`;
      // Return the final file objects in the promise
      return fs.statAsync(path).then(stat => ({
        path,
        name: fileName,
        ext: _.last(fileName.split(".")),
        isDirectory: stat.isDirectory()
      }));
    });
    // Promise.all to wait for all files to finish
    return Promise.all(statPromises);
  })
  .then(finalFiles => console.log(finalFiles));

And the compact version:

fs.readdirAsync(desktopPath)
  .then(fileNames => Promise.all(
    fileNames.map(fileName =>
      fs.statAsync(`${desktopPath}/${fileName}`).then(stat => ({
        path: `${desktopPath}/${fileName}`,
        name: fileName,
        ext: _.last(fileName.split(".")),
        isDirectory: stat.isDirectory()
      })))
  ))
  .then(finalFiles => console.log(finalFiles));

Assuming you're using the latest Node - you can async/await , put async before your function definition and do:

const fileNames = await fs.readdirAsync(desktopPath);
const files = await Promise.map(fileNames, fileName => Promise.props({
  path: `${desktopPath}/${fileName}`,
  name: fileName,
  ext: fileName.split('.').pop(),
  isDirectory: fs.statAsync(`${desktopPath}/${fileName}`);
})));
console.log(files);
return files;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM