I am making AJAX calls in a for
loop and I would like to wait until all of them are done before doing something.
I am trying to use promises but there is something that I'm missing because it won't work. If an error is fired, my when
call is fired immediately and I don't want to. I want to wait until ALL calls are done, no matter if it's success or error.
When all calls are done, I want the number of entries that succeeded and an array of errors.
Here is my code :
for (var i = 0; i < resp.propertiesList.length; i++) {
var documentToSend = {
indexName: indexName,
type: type,
jsonContent: blabla
};
var promise = _TOOLS.Ajax({
type: 'POST',
url: urlTest,
data: documentToSend,
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function (dataResponse) {
numberOfEntriesSuccess++;
},
error: function (xhr, ajaxOptions, errorThrown) {
var errorResponse = JSON.parse(xhr.responseText);
errorsList.push(errorResponse.ExceptionMessage + ". Skipping line.");
}
});
promises.push(promise);
}
//When all ajax calls are finished, load report page and display informations
$.when.apply($, promises).done(function() {
console.log("DONE " + promises.length);
console.log(errorsList);
})
I tried with .done
, .then
, .always
, but none of them fire when I wish to.
I read a lot about promises on the internet but I'm definitely missing something and it's driving me crazy.
Thanks for your help !
The way I find easiest to handle these things is keeping track of the status of each of the Async calls in an object or array. Each time a call completes, flag it as done and check if there are any more running. You could even just add a boolean to your object to check if asyncIsBusy
, or a property for ajaxStatus
.
I am assuming here that indexName
and type
are coming from resp.propertiesList
(you don't seem to use your i
anywhere, so I guess you left that out by accident?).
for (var i = 0; i < resp.propertiesList.length; i++) {
var documentToSend = {
indexName: indexName,
type: type,
jsonContent: blabla
};
sendDocumentAjax(resp.propertiesList[i], documentToSend)
}
function sendDocumentAjax(listObj, documentData){
listObj.ajaxStatus = 'pending';
_TOOLS.Ajax({
type: 'POST',
url: urlTest,
data: documentData,
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function (dataResponse) {
listObj.ajaxStatus = 'success';
},
error: function (xhr, ajaxOptions, errorThrown) {
var errorResponse = JSON.parse(xhr.responseText);
listObj.ajaxStatus = 'error: '+ errorResponse;
}
always: checkAjaxStatuses;
});
}
function checkAjaxStatuses(){
var pending = [];
var successes = [];
var errors = [];
for (var i = 0; i < resp.propertiesList.length; i++) {
if(resp.propertiesList[i].ajaxStatus === 'pending'){
pending.push(resp.propertiesList[i]);
}
if(resp.propertiesList[i].ajaxStatus === 'success'){
successes.push(resp.propertiesList[i]);
}
if(resp.propertiesList[i].ajaxStatus.indexOf('error') !== -1){
errors.push(resp.propertiesList[i]);
}
}
console.log('ajax completed.');
console.log(pending.length + ' pending.');
console.log(successes.length + ' succeeded.');
console.log(errors.length + ' failed.');
}
Note how I used a separate function to send the ajax, so a new closure is created for each object that you sent an ajax call for. You can't do all of this through anonymous functions because i
would always be the maximum value in the callbacks (since the loop finishes before any ajax call is completed), leaving you no way to reference the original object you sent the Ajax for. Using a separate function circumvents this issue.
Q.allSettled
can help you on this, you may find the npm package here - https://www.npmjs.com/package/q.
try setting async: false in ajax call
var promise = _TOOLS.Ajax({
type: 'POST',
url: urlTest,
data: documentToSend,
async: false,
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function (dataResponse) {
numberOfEntriesSuccess++;
},
error: function (xhr, ajaxOptions, errorThrown) {
var errorResponse = JSON.parse(xhr.responseText);
errorsList.push(errorResponse.ExceptionMessage + ". Skipping line.");
}
});
Go for a recursive approach:
ajaxLoopArray(myArray, function(){ /* do something when finish */ });
function ajaxLoopArray(someArray, callbackFunction){
if(someArray.length>0){ //condition to break the loop
data = someArrapy.pop(); // pop removes the last element from an array and returns that element.
$.ajax({
data
}).done(function(x){
//success
})
.fail(function(){
//catch the error here
})
.always(function(){
ajaxLoopArray(someArray, callbackFunction); //recursive call: keep on looping independently of success or error
});
}else{
callbackFunction(); //when everything was done
}
}