16

I would like to iterate over all files located in the HTML 5 file system and have some event get started once the iteration is complete. Since this is async + promises im having a hard time trying to grasp how it should work.

I am using a angularJS and have created a service to encapsulate html 5 file system specific features.

This is the recursive function:

function walkDirectory(path) {

    fileSystem.getFolderContents(path) //this is the services and it returns a promise containing all files in the current folder or directory
        .then(function(entries) {

            for (var i = 0; i < entries.length; i++) {

                if(entries[i].isFile) {
                    console.log("is File: " + entries[i].name);
                    //do something with file here
                } 
                else if (entries[i].isDirectory) {
                    console.log("is Dir: " + entries[i].name);
                    walkDirectory(entries[i].fullPath);
                }
            }
        });
};

ideally i would like to call the function like so, and have it return a promise which gets executed once all files have been traversed.

walkDirectory("/").then( function() {
  console.log(done);
});

Any tips/ ideas how this can be achieved?

an idea would be to have an array of promises and add a new promise to the array for every file/directory. My attempt:

function walkDirectory(path) {

    var defer= $q.defer();
    var promises = [defer.promise];

    fileSystem.getFolderContents(path)
        .then(function(entries) {

            for (var i = 0; i < entries.length; i++) {

                if(entries[i].isFile) {
                    console.log("is File: " + entries[i].name);
                    //do something with file here
                    defer.resolve();
                    promises.push(defer.promise);
                } 
                else if (entries[i].isDirectory) {
                    console.log("is Dir: " + entries[i].name);
                    promises.push(walkDirectory(entries[i].fullPath));
                }
            }
        });

    return $q.all(promises);
};

walkDirectory("/").then(function() {
    console.log("done");
});

This doesn't seem to be working since done is never displayed in the console.

Ivan Bacher
  • 5,855
  • 9
  • 36
  • 56

1 Answers1

11

You're returning the array before you populate it.

Instead, you need to return $q.all(promises) within the then() callback, and return the outer promise:

return fileSystem.getFolderContents(path).then(function(entries) {
    return $q.all(entries.map(function(e) {
        if (e.isFile) {
            // Do something
            return null;  // Don't wait for anything
        } else {
            // Do something
            return walkDirectory(e.fullPath);
        }
    }));
});
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • oh ok cool, so that means i can get rid of var defer= $q.defer() and var promises = [defer.promise], since they are not being used. Anyway your solution seems to work. Thanks. Would you be able to kind of explain your train of thought? – Ivan Bacher Jan 15 '14 at 18:09
  • @BuildingJarl: What don't you understand? https://github.com/kriskowal/q#combination – SLaks Jan 15 '14 at 18:14
  • what is the implementation of walkDirectory given the solution? – cyrf Dec 30 '15 at 02:41
  • 1
    @cyrf: This _is_ `walkDirectory`. – SLaks Dec 30 '15 at 03:22
  • @SLaks: Oh! Thanks. +1, but what does it mean to return $q.all in the successHandler? I was expecting to see `$q.all(mapStuff).then(function(allData) { outerPromise.resolve(allData); })`. Actually, I don't understand 'return anything' from within a successHandler. – cyrf Dec 30 '15 at 03:46
  • this is turning out to be useful (Promise.all) http://www.html5rocks.com/en/tutorials/es6/promises/ – cyrf Dec 30 '15 at 03:56
  • @cyrf: You need to understand promise chaining. You never need to use deferreds or `resolve()` if you already have a promise. Read http://blog.slaks.net/2015-01-05/introducing-promises/#composing-promises – SLaks Dec 30 '15 at 04:20
  • How would you use this to create a data structure representing the directory? – sneilan Aug 15 '16 at 01:41
  • @sneilan: Add a `.then()` to `$q.all()` to turn the array into whatever object you want. – SLaks Aug 15 '16 at 14:41