Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly code a recursive jquery promise

Pardon me if I'm repeating an already very asked question but everything I have seen so far doesn't seem recursive to me, or doesn't map to what we are doing, and the subject of promises and deferred seems to be highly complex for me.

I have a "topic tree" that gets built asynchronously as the user expands nodes. This topic is built using an API endpoint that returns the children of a node on click of the (+) button on the topic tree node.

When the user clicks on button I try to recursively load the topic tree elements using a method that looks like this:

function getAssociatedTopics(){
    $.get('/topics/curriculum-associations', {guids: [...]})
    .then(function(data){

        //Process the topic information here
        var topicId = /* some processing */;

        //Get ancestors of each topic returned
        $.get('/topics/getAncestors', {of: topicId})
        .then(function(data){

            //Process the topic information here
            var topicId = /* some processing */;

            //Get ancestors of each topic returned
            //Rinse repeat as long as we find children of each topic found


        });

    }).then(function(){

        //Expand the nodes
        //Select the nodes

    });
}

So this is what it should look like but I'm all lost reading the documentation to make sure my stuff executes in the right order... The big problem we are having right now with this structure is that my nodes all load in a concurrent way and then erase the selected nodes, open and close nodes any time and the selection ends up super messy.

I don't want an in depth explanation of promises, nor do I want a full solution, but a general idea how to achieve this.

like image 871
Mathieu Dumoulin Avatar asked Dec 18 '15 18:12

Mathieu Dumoulin


1 Answers

First thing first, The complexity of fetching what you need should be handled on the server and not the client. Making hundreds of HTTP requests from the client is a performance disaster waiting to happen.

Now for how you'd do it, two important facts:

  • Promises work by using the return value. $.get returns a promise for the value - that's why you can then off it, but you can also return it to the outside.
  • If you return a promise from a then handler, the promise returns from the then itself will wait for the inner promise to resolve first.

Here is an example illustrating the two points:

$.get("/api").then(function(data) {
    return $.get("/api2/" + data.id);
}).then(function(data2) {
    // because of point #1, we get the data of the inner call here.
});

Now, for your actual problem:

function getAssociatedTopics(guids) {
    // note the `return` here so it can be chained from outside 
    return $.get('/topics/curriculum-associations', {guids: guids})
    .then(function(data){
        var topicId = someProcessing(data);
        // note we return here. I assume this get returns guids. 
        return $.get('/topics/getAncestors', {of: topicId})
    }).then(function (guids) { // this anonymous function can be inlined
        if(guids.length === 0) return; // stop condition
        return getAssociatedTopics(guids); // note the recursive call, remember point #2.
    });
}
getAssociatedTopics(initial).then(function(){
    // all done
});

If you need the results of all the calls, you can chain it off, or you can push to a closure variable array inside the then before the last and access it in the all done handler.

like image 113
Benjamin Gruenbaum Avatar answered Sep 26 '22 16:09

Benjamin Gruenbaum