Rob M
Rob M

Reputation: 1031

Wait for ajax return while in loop jquery

I've been struggling to get this to work using the $.Deferred object. Here is the stubbed setup.

g_plans = [];

$(function(){
   // I have to use a custom ajax function, but it returns a promise obj
    var pageLoadPromise.ajax{(
        url: //call to webmethod
    });

    pageLoadPromise.done(matrix.buildGrid);

    ...
});

(function(matrix,$,undefined){

    var matrix.buildGrid = function(components){
        var gridData = data.d.components;
        buildHeader(gridData);
        buildRows(gridData);
    };

    function buildHeader(components){
            $.each(components,function(key,component){
                //does some stuff
                if(this === that){
                   //do some other stuff
                }
                else{
                    getPlans(copmonent.COMPONENT_ID);
                    //does other stuff
                }
            });
        }
    }

    function buildRows(components){
        //does stuff, but uses data from getPlans
    }

    function getPlans(component){
        var planPromise = ajax({
            url: //call to webmethod
            data:{ componentId:component}
        planPromise.done(function(data){
          g_plans.push({componentId:componentId,plans:data.d.plans});
        });
    }
})(window.matrix = window.matrix || {}, jQuery);   

The problem is buildRows starts before the getPlans has time to finish it's ajax call, which is causing my issue. I have tried a few things with no success. Any help would be appreciated.

Upvotes: 1

Views: 129

Answers (1)

bowheart
bowheart

Reputation: 4676

This is the easiest solution I can see:

1) Have getPlans() return the planPromise promise, which can then be handled back in the buildHeader() function.

function getPlans(component) {
    var planPromise = ajax({
        url: //call to webmethod
        data:{ componentId:component}
    });
    planPromise.done(function(data) {
        g_plans.push({componentId: data.componentId, plans: data.d.plans});
    });
    return planPromise; // return the promise!
}

2) Call buildRows() from the buildHeader() function instead of matrix.buildgrid using the promise returned from getPlans().

function buildHeader(components) {
    var lastPromise;
    $.each(components, function (key, component) {
        lastPromise = getPlans(component.COMPONENT_ID);
    });
    lastPromise.done(function() {
        buildRows(components);
    });
}

Here's a JSFiddle illustrating the basic idea.

The second step is unnecessary, I just found it easier. The other option would be to return the lastPromise from buildHeader(), and handle it back in matrix.buildgrid.

var matrix.buildgrid = function(components) {
    var gridData = components;
    var lastPromise = buildHeader(gridData);
    lastPromise.done(function() {
        buildRows(components);
    });
};

function buildHeader(components) {
    var lastPromise;
    $.each(components, function (key, component) {
        lastPromise = getPlans(component.COMPONENT_ID);
    });
    return lastPromise;
}

EDIT:

To handle the case of lastPromise never getting set as a promise, you'd have to do something like this:

if (lastPromise && typeof lastPromise === 'object') {
    lastPromise.done(function() {
        buildRows(components);
    });
} else {
    buildRows(components); // or whatever needs to happen here.
}

Here's a JSFiddle including that bit in the buildHeader function.

Upvotes: 2

Related Questions