2

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.

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Did you test this ? ;)
@Esailija: Ah, I must have forgotten something. Does it work now (still cannot test)?
Nope, still somehow passes files to readdir which will cause not a directory exception. I made this example for my promise lib: github.com/petkaantonov/bluebird/wiki/… it could be translated to Q by changing map/reduce
1

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) 

2 Comments

Well you are using statSync which makes this code pointless: you might as well *Sync everything and make it even easier. You won't be needing promises either :p.
@Esailija yeah -- you are right. This is mostly for self-teaching, but that was going to be my next step: remove the statSync so that too is async. I'll update when I have that working.
1

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

1 Comment

The link is broken. Here is the correct url. github.com/kriskowal/q-io#listtreepath-guardpath-stat

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.