1

I have searched extensively and can not find an answer that seems to work. I have tried Q.deferred, async.series, async.each, I can not seem to get this sucker to work:

Here is the code, this works, however, the "return subTree" fires for the "tree" export before the recursive is complete. I have validated that the recursive is digging appropriately. I really need the return on recuriveChildren to wait until the recursion call is complete.

exports.tree = function(req, res) {
 var tree = {
  'name': 'New Account'
 };

 var methods = {};

 methods.recursiveChildren = function(path) {
  var subTree = {
   'name': path.field.label+":"+path.match+":"+path.value,
   'id': path._id,
   'parent': path.parent,
   'children': []
  }
  
  Path.find({parent:path._id}).sort({date_created:1}).exec(function (err,childpaths) {
   
   for ( var z in childpaths ) {
    var tmpTree = methods.recursiveChildren(childpaths[z]);
    subTree.children.push(tmpTree);
   }
   
  });
  
  return subTree;
  
 }


 Path.find({parent:null}).sort({date_created:1}).exec(function (err,paths) {
  tree.children = [];
  for ( var x in paths ) {

   var tmpTree = methods.recursiveChildren(paths[x]);
   tree.children.push(tmpTree);
  }

  res.jsonp(tree);
 });
 
};
sol
  • 135
  • 9
  • Async, async, async. You can't do a synchronous return of a result that is built through async operations. You just can't. The function returns long BEFORE the async results are available. You need to either pass in a callback that you can call when all the async operations are done and you finally have the results or you can return a promise that is resolved with the results. – jfriend00 Aug 07 '15 at 04:58
  • @jfriend00 thank you. I appreciate your quick response. I'm new to node, and have struggled getting my head around callbacks, would you be willing to help me stub it in? – sol Aug 07 '15 at 05:01

1 Answers1

1

The confusing thing about asynchronous patterns is that you can't return an actual value, because that hasn't happened yet. You can pass in a function to be executed once the asynchronous operation has completed (callback), or you can return an object that accepts a callback (a promise), that will execute the callback once the operation resolves the promise with a value.

exports.tree = function(req, res) {
  var tree = {
    'name': 'New Account'
  };

  var methods = {};

  methods.recursiveChildren = function(path) {
    var subTree = {
      'name': path.field.label + ":" + path.match + ":" + path.value,
      'id': path._id,
      'parent': path.parent,
      'children': []
    }

    return new Promise(function(resolve, reject) {
      Path.find({
        parent: path._id
      }).sort({
        date_created: 1
      }).exec(function(err, childpaths) {

        Promise
          .all(childpaths.map(function(childpath) {
            /* collect a promise for each child path this returns a promise */
            return methods.recursiveChildren(childpath);
          }))
          .then(function(resolvedPaths) {
            subtree.children = resolvedPaths;
          
             /* the top level promise is fulfilled with the subtree */
            resolve(subTree);
          });
      });
    });
  }


  Path.find({
    parent: null
  }).sort({
    date_created: 1
  }).exec(function(err, paths) {
    Promise.all(paths.map(function(path) {
      return methods.recursiveChildren(path);
    })).then(function(resolvedPaths) {
      tree.paths = resolvedPaths;
      res.jsonp(tree);
    });
  });

};
lyjackal
  • 3,984
  • 1
  • 12
  • 25
  • that worked perfectly... thank you. I had to correct 1 case sensitivity (subtree to subTree). I need to learn promises. THANK YOU. – sol Aug 07 '15 at 11:23