Robert Holden
Robert Holden

Reputation: 141

JQuery Each not waiting for function inside to finish

I have this inside an ajax function

$.each(data['Result'][0], function(Key, Value) {
    InputGen(Value['Type'], Value['Name'], Value['Other'], Value['Value'], function(Html){
        Table = Table + "<tr><td>"+Key+"</td><td>" + Html + "</td></tr>";
    });
});

and InputGen has a callback from another ajax function but when i run this the loop does not seem to be waiting for the ajax to finish. how would i achieve this?

Upvotes: 1

Views: 3811

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074058

...the loop does not seem to be waiting for the ajax to finish.

No, because the "a" in "ajax" stands for asynchronous; it doesn't happen when you make the call, it happens later. But there's no reason the $.each loop would know to sit around and wait for it to finish.

If you don't need it to (e.g., it's okay for the ajax calls to overlap, which normally it should be unless they rely on each other), look at Rocket Hazmat's approach.

If you need each ajax call to wait for the previous one to finish, you can't use $.each (or at least, not the way you were); instead, use an index and respond to the callback by triggering the next request:

// Start with first entry
var index = 0;
var array = data['Result'][0];

// Do the first request
doRequest();

function doRequest() {
    var value = array[index];
    InputGen(value['Type'], value['Name'], value['Other'], value['Value'], function(Html) {
        Table = Table + "<tr><td>"+index+"</td><td>" + Html + "</td></tr>";
        // This request is done, move to the next if any
        if (++index < array.length) {
            doRequest();
        }
    });
}

Side note: Overwhelmingly in JavaScript, variables and non-constructor functions are named starting with a lower-case letter: value rather than Value, etc. So I used index, array, and value above.

Side note 2: value['Type] can be more simply written as value.Type. (And so on.)

Upvotes: 3

gen_Eric
gen_Eric

Reputation: 227200

This is because AJAX is asynchronous. Nothing is going to wait for it to finish. The callback will run in the future at some point when the call is done. By then your $.each (and the code after) is long done.

The solution here is to use promises. That way you can run a callback once all the AJAX calls are done.

You can use jQuery's $.Deferred for this. Without editing the InputGen() function, you can do something like this:

var promises = [];

$.each(data['Result'][0], function(Key, Value) {
    var d = new $.Deferred;

    InputGen(Value['Type'], Value['Name'], Value['Other'], Value['Value'], function(Html){
        d.resolve([Key, Html]);
    });

    promises.push(d.promise());
});

$.when.apply($, promises).done(function(){
    for(var i=0, length=arguments.length; i < length; i++){
        var ele = arguments[i],
            Key = ele[0],
            Html = ele[1];

        Table = Table + "<tr><td>"+Key+"</td><td>" + Html + "</td></tr>";
    }

    // In here is where you can use your updated `Table` variable.
    // You *cannot* use it outside of here, since it was not updated yet
});

Upvotes: 2

Related Questions