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.
source to share
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...
});
source to share
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.
source to share
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();
}
}
});
});
}
});
source to share
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;
});
});
});
source to share