Callbacks in Aysncronous Recursive Methods?

Question!

For a project I am doing I have a function that constructs a Tree model based on a file, and its content. My parse files function is recursive and will keep on calling its self until it can't go any deeper in the directory tree/hit a file that doesn't contain more files. However I don't know how to set up a callback for this function because it is recursive. As of right now I am just timing out my construct tree method but this is a horrible and unreliable solution to my problem. Here is my code:

function constructTree(dir){
  tree = new TreeModel()
  root = tree.parse({name: 'Marshall McGee Sample Pack'});
  parseFiles(dir, root, tree);
  setTimeout(function(){
    root.all().forEach(function(node){
      console.log(node.model.name);
    });
  }, 1000)
}

function parseFiles(dir, parrent, tree){
  fs.readdir(dir, function(err, files){
     if(files){
       for(i = 0; i < files.length; i++){
          parrent.addChild(tree.parse({name: files[i]}));
          console.log(files[i]);
          parseFiles(dir + "/" + files[i], parrent, tree);
       }
     }
  });
}

This code "Works" but horribly. I have no idea how to determine if I have searched an entire directory or how to even do this properly. I hope I explained this well! Thanks and any Help is appricated!!!



Answers

You should add a callback argument to your parseFiles function, and let it call that callback when it has done its job (asychronously).

In order to know when all the items in a certain folder have been traversed, you should keep a count, and only when the last one has completed, notify the caller (via the callback he has provided) that this section of the tree is complete (see the count variable):

function constructTree(dir){
  var tree = new TreeModel()
  var root = tree.parse({name: 'Marshall McGee Sample Pack'});
  // instead of timer, pass a callback function that should be called
  // when the whole tree has been loaded.
  parseFiles(dir, root, tree, function () {
    root.all().forEach(function(node){
      console.log(node.model.name);
    });
  });
}

function parseFiles(dir, parrent, tree, done){
  fs.readdir(dir, function(err, files){
     // Only proceed here when there is at least one file (added condition)
    if(files && files.length){
      // keep track how many items still need to be collected:
      var count = files.length;
      for(var i = 0; i < files.length; i++){
        parrent.addChild(tree.parse({name: files[i]}));
        console.log(files[i]);
        parseFiles(dir + "/" + files[i], parrent, tree, function () {
          count--;
          // if we have all items (recursively) added to the tree, notify the caller
          if (!count && done) done();
        });
      }
    } else {
      // If no information in this folder, notify caller immediately
      if (done) done();
    }
  });
}

As the caller is not obliged to pass a callback, the code has to check the value of done before calling it. That is why there is if (done) done().

NB: Not related to your problem, but you should take care to use the var keyword (or let, const) to avoid the unnecessary creation of global variables.

By : trincot


Every function call that is made is put up on a stack.

Step 1: constructTree 
Step 2: calls parseFiles
Step 3: calls parseFiles
.
.
.
Step n-1: calls parseFiles
Step n: cannot parse any further

At this point, it will start going back

Step n-1
Step n-2
.
.
.
Step 3
Step 2
Step 1 - and pointer returns to the original caller

Let me know, if that solved the issue or you have a different concern.



let firstSeqNum = 5
let secondSeqNum = 9
for (index, number) in array.enumerated() {
    if  number == firstSeqNum && array[index+1] == secondSeqNum {
        print("The sequence \(firstSeqNum), \(secondSeqNum) was found, starting at an index of \(index).")
    }
}

Since there's no built-in method for this, this would be your best option.

By : Bawpotter


This video can help you solving your question :)
By: admin