1

I am creating a function in node.js that loops through the files of a directory. It is supposed to add the file name to the returnData variable, then return the returnData. However, it keeps returning nothing. I've put a few console.log statements in the function to help me debug, but I can't figure out why it won't work.

function loopMusic (directory) {
    var returnData = "";
    fs.readdir (directory, function (err, files) {

        if (err) {
            console.log (err);
        }

        files.forEach (function (file, index) {
            returnData += file;
            console.log (returnData);
        });
    });
    console.log (returnData);
    return returnData;
}

The first console.log statement is able to print the files, but the one right before the return just prints a new line.

  • 2
    You need to do some research on asynchronous code and how callbacks work. – gforce301 Jun 27 '17 at 16:48
  • 1
    `fs.readdir` is async, thus it is called later then the method is returned. The filesystem library in nodejs does alow for synchronous calls which is called `readdirSync` and returns an array of strings. – Shane Jun 27 '17 at 16:50
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – gforce301 Jun 27 '17 at 16:50
  • [Why is my variable unaltered after I modify it inside of a function?](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Jonathan Lonowski Jun 27 '17 at 17:23

2 Answers2

2

You can make the function return a promise:

function loopMusic (directory) {
    return new Promise((resolve, reject) => {
      fs.readdir (directory, function (err, files) {

          if (err) {
            reject(err);
            return;
          }

          files.forEach (function (file, index) {
              returnData += file;
              console.log (returnData);
          });

          resolve(returnData);
    });
}

You would use in that way:

loopMusic('...')
   .then((data) => console.log(data))
   .catch((err) => ...);
Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
0

fs.readdir is asynchronous, meaning it does not return with the result when you call it. Instead the result is provided to the callback, which is called when the command finishes processing. It "calls-back" to the function you provided when it's done (hence the name).

If you wanted to do this synchronously you can do the following:

function loopMusic (directory) {
    var returnData = "";
    var files = fs.readdirSync(directory);
    files.forEach (function (file, index) {
        returnData += file;
        console.log (returnData);
    });
    console.log(files);
    return returnData;
}

That would return a string of mushed together file paths, as in your question.

However, blocking isn't usually a good idea and you should use the asynchronous version. I like to return a Promise in these situations. Here's an example that returns a promise filled with that string. This technically isn't necessary since the callback could just be used...but lets just pretend.

function loopMusic (directory) {
    return new Promise(function(resolve, reject) {
        fs.readdir (directory, function (err, files) {
            if (err) {
                return reject(err);
            }

            let returnData = "";
            files.forEach (function (file, index) {
                returnData += file;
            });

            resolve(returnData);        
        });
    });
}

Usage:

var musicPromise = loopMusic(dir);
musicPromise.then((musicStr) => console.log(musicStr)), (err) => console.log(err));

The asynchronous nature of this makes it a bit hard to follow since things don't happen in order, but when using Promises the then() is used to handle what happens on success (or failure) when it does complete later on.

Finally, if you're using ES2017+ (the newest version of Node) you can use the async/await pattern. Keep in mind my promise example above:

async function loopMusicAsync(directory) {
    try{
        return await loopMusic(directory); //promise returned
    }
    catch(error) {
        console.log(error); //promise rejected
        return null;
    }
}
JNYRanger
  • 6,829
  • 12
  • 53
  • 81