0

I'm trying to do a recursive async loop to trace all the children of a particular object from a third-party lib in nodejs.

Heres the pseudo code:

var tracer = function(nodes){
  var promises [];

  nodes.forEach(function(node){

    // trace returns a promise ...
    var promise = builder.trace(node)
    promises.push(promise);

    promise.then(function(tree){

      // if we had children, get those
      if(tree.children.length){
        promises.push.apply(promises, tracer(tree.children));
      }
    });   

  });

  return promises;
};

RSVP.all(tracer(myArr)).then(function(allTrees){ ... });

but I can't put my finger on how to get them all to resolve correctly and returns the results in one array.

1
  • How does tracer(myArr) even have .then? In this code, you're not returning a promise to use .then on. Commented Jan 15, 2015 at 23:03

2 Answers 2

4

You must not push the recursive promises on the array in the delayed callback. Instead, you'll need to push a promise that represents the recursive results (resolves with those delayed produced promises) right away. Luckily, you even get exactly that back from that then call.

Additionally, I would swap out the each for a map, and do RSVP.all immediately inside the function, for not expecting the caller to deal with that.

function tracer(nodes){
  var promises = nodes.map(function(node){
    // trace returns a promise ...
    var promise = builder.trace(node)
    var recusivePromise = promise.then(function(tree){
      // if we had children, get those
      if (tree.children.length)
        return tracer(tree.children));
      else
        return node;// the leaf node itself
    });
    return recusivePromise; // which will resolve with the `tracer(…)` result
                            // or the leaf
  });
  return RSVP.all(promises);
}

tracer(myArr).then(function(allTrees){ … });
Sign up to request clarification or add additional context in comments.

3 Comments

Note: This return a lists of lists vs a flat array.
@amcdnl: Yes, it's supposed to match the tree structure. If you want a flat array, make each recursivePromise resolve with an array (by returning leafs in singleton arrays), and add a concat step after .all().
@DanKanze: It's basic functional programming :-)
0

I ended up going with a counter type approach ...

var traceDeps = function(parents, cb){
  var count = 0, 
    trees = [], 
    trace = function(nodes){
      nodes.forEach(function(node){
        count++;
        builder.trace(node).then(function(tree){
          trees.push(tree);

          if(tree.children.length){
            trace(tree.children);
          }

          count--;
          if (count === 0) cb(trees);
        });
      });
    };

  trace(parents);
};

traceDeps(myArr, function(trees){ ... });

4 Comments

Why so complicated? No need to use callbacks and counters when you can use promises. Also you are ignoring errors with this approach.
If any of the trace() promises is getting rejected, you won't notice (and instead wait indefinitely for your callback that is never called)
This solution is inferior to Bergi's and ignores errors. You should consider using his instead.
Ya, I agree. Thanks for the post @Bergi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.