Search code examples
javascriptnode.jses6-promise

How to handle promises while using recursive functions in JavaScript


I want to log all the files in the directory recursively and return a confirmation when all files are logged. Here's the directory structure.

sample │ app.js └───Temp1 │ │ temp1.js │ └───Temp2 │ │ temp2.js

Here's the code

```

let readDirectory = function(dirname){
    return new Promise((resolve,reject)=>{
         fs.readdir(dirname,(err,files)=>{
            if(err) reject(err);
            files.forEach(file=>{
                fs.stat(`${dirname}/${file}`,(err,stats)=>{
                    if(stats.isDirectory()){
                        readDirectory(`${dirname}/${file}`)
                    }else{
                        resolve(console.log(file));                      
                    }
                })                
            })
        })
    })   
}



readDirectory(sampledir).then(()=>console.log('completed'));

```

Below is the result when I execute this function.

```

app.js
completed
temp1.js
temp2.js

```

Where should I resolve in order to get output as below.

```

app.js
temp1.js
temp2.js
completed

```


Solution

  • You need to resolve only after Promise.all resolves over every file. Also, you need to resolve each directory promise only after each file in the directory is finished:

    let readDirectory = function(dirname) {
      return new Promise((resolveAll, rejectAll) => {
        fs.readdir(dirname, (err, files) => {
          if (err) rejectAll(err);
        })
      }).then((files) => {
        const filesPromises = files.map(file => (
          new Promise((resolveFile, rejectFile) => {
            fs.stat(`${dirname}/${file}`, (err, stats) => {
              if (err) rejectFile(err);
              if (stats.isDirectory()) {
                readDirectory(`${dirname}/${file}`)
                 .then(resolveFile);
                 .catch(rejectFile);
              } else {
                resolveFile(console.log(file));
              }
            })
          })
        ));
        return Promise.all(filesPromises).then(resolveAll);
      });
    }
    

    Note that this is pretty difficult to make sense of - you'll be far better off using async/await instead, something like this:

    let readDirectory = async function(dirname) {
      const files = await new Promise((resolve, reject) => {
        fs.readdir(dirname, (err, files) => {
          if (err) reject(err);
          resolve(files);
        });
      });
      const filesPromises = files.map(async (file) => {
        const stats = await new Promise((resolve, reject) => {
          fs.stat(`${dirname}/${file}`, (err, stats) => {
            if (err) reject (err);
            else resolve(stats);
          });
        });
        if (stats.isDirectory()) await readDirectory(`${dirname}/${file}`);
        else console.log(file);
      });
      return Promise.all(filesPromises);
    }