Wait for all ajax requests to complete.

I need to wait until all my ajax functions are done and then proceed with exclusion.

In my particular case, I need to translate some fields in the form before submitting. I am translating them with an ajax call to an external site. Depending on some values ​​in the form, I will need to do more or less translations. When all the translations are done (if any), I have to validate the form with ajax and, if valid, then submit.

This is my approach:
First, I have a function that sends an ajax call and does stuff with the data received:

function translate(...) {
    $("#ajaxCounter").val(parseInt($("#ajaxCounter").val()) + 1);
    $.ajax({
        ...
        success:function(data) {
            ...
            $("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
        }
    });

      

Then, when the form needs to be submitted, I execute the following code:

$("#form").submit(function() {
    translatable_fields.each(function() {
        translate(...);
    });
    while (parseInt($("#ajaxCounter").val()) > 0) { null; }
    if (!(this).hasClass('ready')) {
        $.ajax({
            //validation
            success: function(data) {
                if (data['isValid']) {
                    $("#form").addClass('ready');
                    $("#form").submit();
                }
            }
        });
    }
    return true;
});

      

The problem is that the loop while

in the send function never ends.

If I execute the code without a loop while

, I see that the input ajaxCounter

increases when the translation functions start and decreases when they end.

+3


source to share


4 answers


You can achieve this much more neatly by using objects deferred

returned from the call $.ajax

. You must first return a function translate()

to return deferred

:

function translate(...){
    return $.ajax({
        // settings...
    });
});

      

Then you can put all of these promises into one array:



var requests = [];
translatable_fields.each(function(){
    requests.push(translate(...));
});

      

Then you can apply

set this array to $.when

:

$.when.apply($, requests).done(function(schemas) {
     console.log("All requests complete");
    // do something...
});

      

+12


source


You can do this using deferred objects, but you don't need to use $.when.apply

with an array if you're only interested in finalizing.

Instead, you can bind the parallel promises using the template promise = $.when(promise, another promise)

Modify your translation to return the Ajax promise:

function translate(...) {
    ...
    return $.ajax({
        ...
    });
}

      



and your promise loop will be just:

var promise; // Start with an undefined promise - which is the same as a resolved promise for $.when
translatable_fields.each(function() {
    promise = $.when(promise, translate(...));
});

// Wait for all promises to complete
promise.done(function(){
    // now do the final code after all the ajax calls complete
});

      

Notes:

  • This creates an extra promise per call $.when

    , but the overhead is very small and the resulting code is pretty simple.
+1


source


No, you can't just quote like that: callbacks will never get the chance to be called.

I would do something like this:

function translateAllFields(done) {
    var requestsInProgress = 0, doneCalled = false;
    translatable_fields.each(function () {
        ++requestsInProgress;
        $.ajax({
            //...
            success: function (data) {
                //...
                $("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
            }
        }).always(function () {
            if (--requestsInProgress === 0) {
                done();
                doneCalled = true;
            }
        });
    });
    if (requestsInProgress === 0 && !doneCalled) {
        // in case translatable_fields was empty
        done();
    }
}

      

and then:

$("#form").submit(function (e) {
    if (!(this).hasClass('ready')) {
        e.preventDefault();
        e.stopPropagation();
        translateAllFields(function() {
            $.ajax({
                //validation
                success: function (data) {
                    if (data['isValid']) {
                        $("#form").addClass('ready');
                        $("#form").submit();
                    }
                }
            });
        });
    }
});

      

0


source


You can use a callback

function translate(..., callback) {
    $.ajax({
        ...
        success:function(data) {
            ...
            callback(data);
        }
    });
};

      

And pass it after the ajax code

$("#form").submit(function() {
    translatable_fields.each(function() {
        translate(..., function(result){

            if (!(this).hasClass('ready')) {
                $.ajax({
                    //validation
                    success: function(data) {
                        if (data['isValid']) {
                            $("#form").addClass('ready');
                            $("#form").submit();
                        }
                    }
                });
            }
            return true;
        });
    });
});

      

-2


source







All Articles