lucidquiet
lucidquiet

Reputation: 6558

Is there an example of using raw Q promise library with node to recursively traverse a directory asynchronously?

I am aware of io-q, the library that does async IO resulting in promises. But I'm looking for a simple example of using the Q library to recursively traverse a directory structure, where the final result is a list of all the files in all the directories starting in the folder provided to some function, but flattened in the process to a single array of file names.

Is there an example of this out there? Or perhaps there's an example that isn't recursive, which is fine. I'm guessing this is pretty simple, but this is my first exposure to both async/promises.

Upvotes: 2

Views: 1585

Answers (3)

Stuart K
Stuart K

Reputation: 3292

The listTree function from Q-IO does exactly what you're looking for, so you could take a look at the implementation.

Upvotes: 1

Bergi
Bergi

Reputation: 665584

I found this gist which does what you want and is easy to promisify:

var Q = require('q'),
    fs = require('fs'),
    p = require('path');
function readDir(path) {
    return Q.nfcall(fs.lstat, path).then(function(stat) {
        if (stat.isDirectory()) {
            return Q.nfcall(fs.readdir, path).then(function(files) {
                return Q.all(files
                // .map(p.join.bind(p, path)).map(readDir)
                .map(function(file) {
                    return readDir(p.join(path, file));
                })
                ).then(
                // Function.apply.bind(Array.prototype.concat, [])
                function(results) {
                    return [].concat.apply([], results);
                });
            });
        } else {
            return [path];
        }
    });
}

It uses nfcall to get promises for the filesystem API and Q.all to wait for all subdirectory results before concatenating them.

Upvotes: 4

lucidquiet
lucidquiet

Reputation: 6558

Well, this is the solution I finally arrived at (CoffeeScript). I'm not a Node or Q expert so I guess this will do for now. My solution actually flattens the list, and the way to remove the directories from the output is to remove the .then() after the read(f).

Q = require('q')
fs = require('fs')
_ = require("lodash")

isFile = (name) ->
    fs.statSync(name).isFile()

withDir = (dir) ->
    (files) -> _.map(files, (f) -> "#{dir}/#{f}")

read = (dir) ->
    Q.nfcall(fs.readdir, dir)
        .then(withDir dir)
        .then((files) -> Q.all(toPromises files))

toPromises = (files) ->
    for f in files
        if isFile f then Q(f) else read(f).then((m) -> m.concat("d " + f))

read("my-root-dir")
    .then((files) -> _.flatten(files))
    .then((r) -> console.log r)

Upvotes: 1

Related Questions