3

I'm trying to iterate a given directory recursively using Node and Bluebird (the promises library) but as far as it seems it doesn't work correctly.

When I uncomment the 'console.log' line below, it seems like I'm getting the correct results but I'm not sure what's going on because the final result I'm getting from the consuming code below is just the first file in the first directory.

Maybe the issue is not in the iterate function itself but in the way I'm consuming it.

I'm kinda new to promises so maybe I'm just approaching it in the wrong way.

Here is the code I wrote.

import * as Path from "path";
import * as Promise from "bluebird";
const FS: any = Promise.promisifyAll<any>((require("fs")));
export class Dir {
  public static iterate(path: string) {
    return new Promise((resolve, reject) => {
      FS.lstatAsync(path).then(stat => {
        if (stat.isDirectory()) {
          FS.readdirAsync(path).each(name => {
            let fullPath = Path.join(path, name);
            // console.log(fullPath);
            FS.lstatAsync(fullPath).then(stat => stat.isDirectory() ? Dir.iterate(fullPath) : resolve(fullPath));
          });
        } else {
          reject(`The path '${path}' is not a directory.`)
        }
      });
    })
  }
}

Here is the way I'm using/consuming it

Dir.iterate(ProjectPaths.client)
.then(f => {
    console.log(f)
    return f;
})
.catch(e => console.log(e));

EDIT: Just to clarify I'm using TypeScript! forgot to mention it in my post.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
iam3yal
  • 2,188
  • 4
  • 35
  • 44
  • What is the desired output of your function? It looks like you're trying to iterate recursively, but only resolving with a single path in the end. And, you aren't returning any of the inner promises so the code isn't waiting on them. – jfriend00 Sep 13 '15 at 06:17
  • Well, I want to get all the files and directories at the consumer so I can do some stuff with it like renaming or deleting files/directories. – iam3yal Sep 13 '15 at 09:23
  • 2
    Hey, your code has [the explicit construction antipattern](stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it), bluebird actually contains an example of iterating directories here: https://github.com/petkaantonov/bluebird/wiki/Snippets#reading-directory-and-sub-directory-contents-recursively – Benjamin Gruenbaum Sep 13 '15 at 12:27

1 Answers1

5

Based on Petka's code at the bluebird API docs, here is an annotated example of iterating a directory recursively with bluebird:

function readDir(dirName) { // reading a directory requires its name
    return fs.readdirAsync(dirName).map(fileName => { // for each file we take it
        var path = Path.join(dirName, fileName); // get the correct path
        // if it's a directory we scan it too recursively, otherwise we just add it
        return fs.statAsync(path).then(stat => stat.isDirectory() ? readDir(path) : path);
    }).reduce((a, b) => a.concat(b), []); // we flatten the result to an array
}

Or in more "clever" ES2015 style:

const readDir => dirName => fs.readdirAsync(dirName).map(fileName => { 
        var path = Path.join(dirName, fileName); 
        return fs.statAsync(path).then(stat => stat.isDirectory() ? readDir(path) : path);
    }).reduce((a, b) => a.concat(b), []);

There is no need for explicit construction as promises chain. Your attempt failed because you were resolving the outer promise on every single inner file, promises resolve only once.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Okay, everything is working but how should I handle cases where I need to throw an error? can you give an example? like in the example I gave where I used reject, should I use } else { throw ... } as I would normally do? or there some better way to do that in bluebird? – iam3yal Sep 13 '15 at 15:19
  • @jfriend00 I'm using TypeScript, I've updated my OP and made a note about it, thanks!. :) – iam3yal Sep 13 '15 at 16:55
  • You should `throw` like you normally would - promises have error propagation :) – Benjamin Gruenbaum Sep 13 '15 at 17:37