0

I want to use then(), as seen below, after my code has finished recursing the folder hierarchy.

Everything works great, particularly the use of

  const fileNames = await fs.promises.readdir(dir);

and

  const stat = await fs.promises.stat(thing_path);

However, the recursive part I do not know how to implement correctly so that

getFolderInfo(start).then((all_data)=>{
  console.log('Done recursing the directory');  
});

works correctly.

Complete File

const fs = require('fs');
const path = require('path');

// Configure & Initialize
let start = '_t/';
let depth = 1;
let counter = 0;
let total_size = 0;

// Definition
async function recurseFolders(dir, callback) {
  const fileNames = await fs.promises.readdir(dir);
  const listings = await Promise.all(
    fileNames.map(async (thing_name) => {
      const thing_path = path.join(dir, thing_name);
      const stat = await fs.promises.stat(thing_path);
      return {name: thing_name, path: thing_path, size: stat.size, isFolder: stat.isDirectory()};
    })
  );
  listings.forEach(iterate);
  function iterate (listing) {
    counter++;
    total_size += listing.size;
    listing.counter = counter;
    listing.total_size = total_size;
    callback(listing);
    if (listing.isFolder) {
      recurseFolders(listing.path, callback);
    }
  }
}

async function getFolderInfo (start) {
  await recurseFolders(start, (data) => {
    console.log(data);
  });
}

getFolderInfo(start).then((all_data)=>{
  console.log('Done recursing the directory');  
});
  • The recursive `recurseFolders` call doesn't have an `await`; is this intentional? –  May 15 '19 at 01:41
  • I added an **await** to the the call and made the function `iterate` **async**, but it still does not have the deisired behavior. –  May 15 '19 at 01:47
  • This is node code is there a way to make it run in the browser on stackoverflow? –  May 15 '19 at 01:48
  • What is the observed behavior? You aren't populating `all_data` right now btw. –  May 15 '19 at 01:48
  • Where the console logs "Done recursing the directory" it should be done recursing the directory. `all_data` is just a place holder for when it starts working. –  May 15 '19 at 01:49
  • You have to return something at the end for `all_data` to have a value. You're not returning anything, so `all_data` will always be `undefined`. You also need to `await` the recursive calls and merge the results into `all_data`. The `callback` argument also seems a bit strange, is this a temporary debugging thing? It's also not clear how `depth`, `counter`, and `total_size` are expected to be used and why they are declared outside of the method. Can you clarify what you mean by "works correctly" with your expectations, perhaps an example of what you want `all_data` to look like? – Jake Holzinger May 15 '19 at 03:06
  • @J.M. I think this [Q&A](https://stackoverflow.com/a/55225285/633183) will be helpful for you. Let me know if you have follow-up questions. – Mulan May 15 '19 at 03:20

3 Answers3

1

The problem tidys up nicely if you put the fs work in its own function. This function runs stat on a given path and answers a modified stat object for each entry...

async function getStats(path) {
  const names = await fs.promises.readdir(path);
  let promises = names.map(async name => {
      const pathName = path.join(path, name)
      const stat = await fs.promises.stat(pathName);
      return { name: name, path: pathName, size: stat.size, isFolder: stat.isDirectory() };
  })
  return await Promise.all(promises)
}

Now, just call getStats for a given path, apply the callback to each result, and either recurse on contained directories or do nothing...

async function recurseFolders(path, callback) {
    const stats = await getStats(path)
    let promises = stats.map(async stat => {
        callback(stat)
        return await stat.isFolder ? recurseFolders(stat.path, callback) : Promise.resolve()
    })
    return await Promise.all(promises)
}

That's it.

danh
  • 62,181
  • 10
  • 95
  • 136
0
var fs = require('fs');
var path = require('path');
var getFolderInfo = function (dir, done) {
    var results = [];
    fs.readdir(dir, function (err, list) {
        if (err) return done(err);
        var pending = list.length;
        if (!pending) return done(null, results);
        list.forEach(function (file) {
            file = path.resolve(dir, file);
            fs.stat(file, function (err, stat) {
                if (stat && stat.isDirectory()) {
                    getFolderInfo(file, function (err, res) {
                        results = results.concat(res);
                        if (!--pending) done(null, results);
                    });
                } else {
                    results.push(file);
                    if (!--pending) done(null, results);
                }
            });
        });
    });
};

getFolderInfo('/home/username', function (err, results) {
    if (err) throw err;
    console.log(results);
});
Wang Liang
  • 4,244
  • 6
  • 22
  • 45
0

You can simplify this down if you do not attempt to flatten the folder hierarchy into a list. I made some assumptions about how you were expecting counter and total_size to be used. I also ignored the unused depth parameter.

const fs = require('fs');
const path = require('path');

async function recurseFolders(folderPath) {
    const fileNames = await fs.promises.readdir(folderPath);
    const fileData = await Promise.all(
        fileNames.map(async (fileName) => {
            const filePath = path.join(folderPath, fileName);
            const fileStat = await fs.promises.stat(filePath);
            if (fileStat.isDirectory()) {
                return recurseFolders(filePath);
            }
            return { 
                name: fileName, 
                path: filePath,
                size: fileStat.size,
                isFolder: false
            };
        })
    );
    const folder = {
        name: path.basename(folderPath),
        path: folderPath,
        files: fileData,
        isFolder: true,
        count: fileData.length,
        size: 0
    };
    return fileData.reduce((total, file) => {
        total.size += file.size;
        if (file.isFolder) {
            total.count += file.count;
        }
        return total;
    }, folder);
}

recurseFolders('some/path/to/folder').then((file) => {
    console.log(file);
}).catch((err) => {
    process.exitCode = 1;
    console.error(err);
});

This implementation will return a data structure that looks something like this:

{
    "name": "demo",
    "path": "example/path/demo",
    "files": [
        {
            "name": "dir1",
            "path": "example/path/demo/dir1",
            "files": [
                {
                    "name": "dir2",
                    "path": "example/path/demo/dir1/dir2",
                    "files": [
                        {
                            "name": "file3.txt",
                            "path": "example/path/demo/dir1/dir2/file3.txt",
                            "size": 7412,
                            "isFolder": false
                        }
                    ],
                    "isFolder": true,
                    "count": 1,
                    "size": 7412
                },
                {
                    "name": "file2.txt",
                    "path": "example/path/demo/dir1/file2.txt",
                    "size": 8364,
                    "isFolder": false
                }
            ],
            "isFolder": true,
            "count": 3,
            "size": 15776
        },
        {
            "name": "file1.txt",
            "path": "example/path/demo/file1.txt",
            "size": 6870,
            "isFolder": false
        }
    ],
    "isFolder": true,
    "count": 5,
    "size": 22646
}
Jake Holzinger
  • 5,783
  • 2
  • 19
  • 33