OlaStein
OlaStein

Reputation: 25

Read files asynchronously

I am trying to read some files and store their name in an array in nodejs. I have made a function. It reads the file, and prints them out in the console, but the output of the array is []. I am assuming it is because node.js is asynchronous, but I thought a callback would fix it?

readCurdir("./uploads/", curDirReadFinn); 
function readCurdir(dir, callback){
  var tmpArray = []; 
  fs.readdir(dir, function(err, files){
    if(err){
      if(err.code==='EISDIR'){}
      else{
        console.log(err);
      }
      return; 
    }
    files.forEach(function(file){
      fs.readFile(dir + file, 'utf-8', function(err, data){
        if(err){
          if(err.code==='EISDIR'){}
          else{
            console.log(err);
          }
          return; 
        }
        tmpArray.push(file); 
        console.log(file);   //this prints
      }); 
    }); 
  }); 
  callback(tmpArray); 
} 
function curDirReadFinn(array){ 
    console.log(array); //Output: []
}

I have also tried this:

files.forEach(function(file){
  fs.readFile(dir + file, 'utf-8', function(err, data){
    if(err){
      if(err.code==='EISDIR'){}
      else{
        console.log(err);
      }
      return; 
    }
    tmpArray.push(file); 
    counter++; 
    if(counter === files.lenght){
    callback(tmpArray); 
    }
  }); 
});

Upvotes: 1

Views: 149

Answers (2)

OrangeDog
OrangeDog

Reputation: 38826

You've got the right idea with your second attempt. After I'd simplified it, it then works perfectly:

function readCurdir(dir, callback){
  var tmpArray = []; 

  fs.readdir(dir, function(err, files) {
    if (err) {
      return console.log(err);
    }

    var counter = files.length;

    files.forEach(function(file) {
      fs.stat(path.join(dir, file), function(err, stats) {
        if (err) {
          console.log(err);
        }
        else if (stats.isFile()) {
          tmpArray.push(file); 
        }

        --counter === 0 && callback(tmpArray);
      }); 
    });
  }); 
} 

You can simplify it even further (and propagate errors more easily) using the async module:

async.each(files, function(file, cb) {
  fs.stat(path.join(dir, file), function(err, stats) {
    if (err) {
      return cb(err);
    }
    else if (stats.isFile()) {
      tmpArray.push(file); 
    }

    cb();
  }); 
}, function(err) {
    callback(err, tmpArray)
});

Upvotes: 2

Quirk
Quirk

Reputation: 1337

Asynchronous code strikes again! fs.readdir() is asynchronous. Your callback method is called separate from the execution of fs.readdir(). That's why your tmpArray is empty when you log it in your callback method curDirReadFinn().

There are some ways to fix your problem:

  1. Ready your directory and files synchronously. NodeJS has methods for that. See here: https://nodejs.org/api/fs.html#fs_fs_readdirsync_path_options

  2. As @Thomas mentioned, you can make use of stat and isFile() to get it over with faster. In general eliminate the need for heavy asynchronous code.

  3. Use Promises!

Upvotes: 1

Related Questions