0

A tree api returns childrens when node element is passed to it.

I pass the root node first and then on the basis of the nodes returned I pass all of them back recursively, if they have hasChildren parameter true.

Is there a way to know when the function has finished creating the tree.

function recursivelyFetchChildren(id, selectedAsset, parentId){
    return ajaxCall(id, selectedAsset, parentId)
    .then(function(data){
        //collects all childs in childs array
        childs.push(data);
        for(var i=0; i<data.length; i++){
            if(data[i].HasChildren){
                return recursivelyFetchChildren(id,selectedAsset,data[i].Id);
            }else{
                //this return statement prematurely completes the promise
                return data[i];
            }
        }
    });
}

recursivelyFetchChildren(id, selectedAsset, parentId).then(function(){
    print(childs)  //prints the childs before all the promises have resolved
});

Can anyone suggest an approach where I can make the recursivelyFetchChildren function wait for rendering the complete tree?

Birju Shah
  • 1,220
  • 9
  • 18

1 Answers1

2

Go the other way, and return a function that is resolved when all of its children are resolved.

{
  id: "123",
  data: {
    children: [
      { id: "234", value: 2, data: { children: [] } },
    ]
  }
}


const loadData = node => getData(`/url/${node.id}`).then(data => {
  return loadChildren(data.hasChildren ? data.children : [])
    .then(children => {
      data.children = children;
      node.data = data;
      return node;
    });
});
const loadChildren = children => Promise.all(children.map(loadData));

Because I am returning and chaining promises, the outermost one that I return won't resolve, until the inner-most one is finished.

Personally, I would be likely to build a new node, rather than modify the one that I had, but that's a different matter altogether.

EDIT

Mutual Recursion: two functions that call one another.

function a () { return b(); }
function b () { return a(); }

If you have a tree where each root has an array of children, then you can write two functions to bounce back and forth between one another: one to deal with a node, one to deal with an array of nodes. There are other examples, of course, but one is going to be the recursive function you want, and the other is going to be a loop, or something that lets you call the recursive function on one thing at a time... usually; again, there are other cases.

const tree = {
  value: 1,
  children: [{
    value: 2,
    children: []
  }, {
    value: 3,
    children: []
  }]
};

const sumChildren = children =>
  children
    .map(sumNode)
    .reduce((x, y) => x + y, 0);

const sumNode = node =>
  node.value + sumChildren(node.children);

const total = sumNode(tree); // 6

Promise Resolution

One of the things that's usually missed, in terms of promises, is that you can return a promise, inside of a .then and cause the resolution of the promise to wait longer. When it resolves, it will have the value that b resolved with (or the error that b had)

eventuallyGetA()
  .then(a => eventuallyGetB(a.data))
  .then(b => console.log(`I waited for ${b}`));

You can even do something like

const wait = ms =>
  new Promise(resolve => setTimeout(resolve, ms));

doX()
  .then(() => wait(2000))
  .then(doY)
  .then(() => wait(3000))
  .then(doZ);

It should be pretty straightforward as to what that sequence is going to do.

Hope that helps.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • But how do I put the condition to make the ajax call only if it has the HasChildren property true. Can you edit my example and explain. I am not able to relate to the example you gave. – Birju Shah Apr 25 '17 at 05:15
  • 1
    @BirjuShah It doesn't make the AJAX call if you have the flag... it makes the AJAX call if you call the function on a node. If a node has no children in its array, then it has no children to loop through. And if it can't loop through any, then it won't call the AJAX function, because it won't recursively descend. Which means that the HasChildren property is probably pretty useless, when an empty list serves the same purpose. But just to be explicit, I added a `hasChildren` check to my example, which doesn't actually add any safety; an empty array is enough. – Norguard Apr 25 '17 at 05:37
  • Thanks. It works. However I had a tough time getting my head around it. It would be great if you can post some article explaining this concept. – Birju Shah Apr 25 '17 at 14:16
  • 1
    @BirjuShah there are two concepts: mutual recursion (or "A - B recursion"), and that if you return a promise inside of a `.then` callback the outer promise doesn't finish until the inner promise finishes. I admit that it's pretty confusing if you are coming at this from the standpoint of not having a grasp of both at the same time; but there are lots of examples of both in isolation. If you learn about mutual recursion for dealing with trees, and you learn about chaining promises even by returning promises inside a `.then`, then you have all of the pieces you need to put this together. – Norguard Apr 26 '17 at 05:53
  • Thanks for the explanation. Will surely look in to both the concepts. – Birju Shah Apr 26 '17 at 07:28
  • 1
    @BirjuShah made an edit; hope you find it useful to start. – Norguard Apr 26 '17 at 13:53