Miha Šušteršič
Miha Šušteršič

Reputation: 10042

bluebird - function returns promise objects instead of actual data

Following this snippet I am trying to write a function that loops trough a directory, finds directories, and reads xml file names from those directories (I know that the folder structure will always remain the same). So far my function is working as expected, but when I try to get the return from the function I just get Promise objects.

My code:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const path = require('path');

function getFileNames(rootPath) {
  // Read content of path
  return fs.readdirAsync(rootPath)
    // For every file in path
    .then(function(directories) {
      // Filter out the directories
      return directories.filter(function(file) {
        return fs.statSync(path.join(rootPath, file)).isDirectory();
      });
    })
    // For every directory
    .then(function(directories) {
      return directories.map(function(directory) {
        // Read file in the directory
        return fs.readdirAsync(path.join(rootPath, directory))
          .filter(function(file) {
            return path.extname(file) == '.XML';
          })
          .then(function(files) {
            // Do something with the files
            return files;
          });
        });
    });
}

getFileNames('./XML').then(function(files) {
  console.log(files);
});

When I console.log(files) inside the last .then function inside getFileNames, I get the actual arrays of file names in the console. But when I run the code above I get this output:

[ Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined },
  Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined },
  Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined },
  Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined },
  Promise {
    _bitField: 0,
    _fulfillmentHandler0: undefined,
    _rejectionHandler0: undefined,
    _promise0: undefined,
    _receiver0: undefined } ]

Why is this happening and how to fix it?

Upvotes: 3

Views: 571

Answers (4)

Miha Šušteršič
Miha Šušteršič

Reputation: 10042

Figured it out, there was one .then too much on the second function:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const path = require('path');

function getFileNames(rootPath) {
  // Read content of path
  return fs.readdirAsync(rootPath)
      .then(function(content) {
        return content.filter(function(file) {
          return fs.statSync(path.join(rootPath, file)).isDirectory();
        });
      })
      // For every directory
      .map(function(directory) {
        // Read files in the directory
        return fs.readdirAsync(path.join(rootPath, directory))
            .filter(function(file) {
              return path.extname(file) == '.XML';
            });
      });
}

getFileNames('./XML').then(function(files) {
  console.log(files);
});

Upvotes: 0

Bergi
Bergi

Reputation: 664297

In the lines

.then(function(directories) {
  return directories.map(function(directory) {
    return fs.readdirAsync…

you are creating a promise for an array of promises, and that's exactly what you're getting in your final log. Instead of returning an array of promises, you need to return a promise for an array of values - and Promise.all is exactly what does that:

.then(function(directories) {
  return Promise.all(directories.map(function(directory) {
    return fs.readdirAsync(…)
    …
  }));
})

However, in Bluebird it would be more idiomatic to use Promise.map(directories, function(…) { … }) or even the map method (similar to how you already used .filter did on the files in each directory):

function getFileNames(rootPath) {
  return fs.readdirAsync(rootPath)
  .filter(function(file) {
    return fs.statAsync(path.join(rootPath, file)).then(function(s) {
      return s.isDirectory();
    });
  })
  .map(function(directory) {
//^^^^
    return fs.readdirAsync(path.join(rootPath, directory))
    .filter(function(file) {
      return path.extname(file) == '.XML';
    })
    .map(function(file) {
      // Do something with every file
      return file;
    });
  });
}

Upvotes: 2

Tamas Hegedus
Tamas Hegedus

Reputation: 29906

This piece of code:

.then(function(directories) {
   return directories.map(function(directory) {
     return fs.readdirAsync(path.join(rootPath, directory))
     ...

will return an array of promises to the then callback. An array of promises count as an immediate value here, will not be coerected to a Promise of an array. To convert an array of promises to a promise of an array you could use Promise.all, but since you are using bluebird, you have a better option:

All you have to do is to use Promise.map:

.then(function(directories) {
   return Promise.map(directories, function(directory) {
     return fs.readdirAsync(path.join(rootPath, directory))
     ...

Upvotes: 0

stasovlas
stasovlas

Reputation: 7406

try this

return getFileNames('./XML').then(function(files) {
    console.log(files);
    return files;
});

Upvotes: 0

Related Questions