I have a coding issue where I want to loop thru and call an ajax call but I dont want another request to be sent until the first one is complete. I have tried setting it to asyc = false and adding an onsuccess callback. But it seems like the loop is continuing to run which gives me responses out of order and parallel requests.
// This function is used to generate a numeric val and passes it along in the success callback
function duplicateOmsid(totalAmount, omsid) {
var url = '/portal/GetBulkCopyAmountServlet';
var errorString;
new Ajax.Request(
url, {
method: 'post',
parameters: {
totalAmount: totalAmount,
omsid: omsid
},
async: false,
onSuccess: function(transport) {
dataResponse = transport.responseText.evalJSON();
createWorkflow(totalAmount, omsid, dataResponse);
},
.....
// Function used to loop thru and call the duplicate workflow ajax call
function createWorkflow(totalAmount, omsid, bulkAmount) {
var amountProccessed = 0;
for( i = 0; amountProccessed < totalAmount; i++ ) { // Loop through source
var duplicateAmt;
if (totalAmount < 11){
duplicateAmt = totalAmount
}else{
duplicateAmt = amountProccessed + dataResponse < totalAmount ? dataResponse : totalAmount - amountProccessed
}
duplicateWorkflow(totalAmount, omsid, duplicateAmt, amountProccessed);
amountProccessed += bulkAmount;
}
}
// Function used to create the workflow ajax call - the success handler is updating the user.
function duplicateWorkflow( totalAmount, omsid, bulkAmount, amountProccessed){
amountProccessed += bulkAmount;
var url = '/portal/CreateWorkFlowServlet';
var errorString;
new Ajax.Request(
url, {
method: 'post',
parameters: {
totalAmount: totalAmount,
omsid: omsid,
bulkAmount: bulkAmount
},
async: false,
onSuccess: function(transport) {
var div = document.getElementById('progress');
if( amountProccessed > totalAmount){
div.innerHTML = totalAmount + ' out of ' + totalAmount + ' Processed ' ;
alert (totalAmount + 'Items successfully duplicated ')
}else{
div.innerHTML = amountProccessed + ' out of ' + totalAmount + ' Processed ' ;
}
},
onFailure: function(e) {
}
},
onException: function(e) {
}
},
});
}
As a rule of thumb, the way to sequentialize async code using raw Javascript is to use recursion instead of a for loop.
var urls = [ /*...*/ ];
function loop(i, onDone){
if(i >= urls.length){
//base case
onDone( theResultOfProcessingTheAjaxRequests );
}else{
Ajax.Request(urls[i], {
onsuccess: function(){
loop(i+1, onDone);
}
});
}
}
loop(0, function(result){
console.log("all done");
});
Note that I converted i
to a function parameter, to keep it scoped to the looping function. If you wanted, you could declare it outside, just like you did in the for loop:
var urls = [ /*...*/ ];
var i = 0;
function loop(onDone){
//...
i = i+1;
loop(onDone);
}
Additionally, I added an "onDone" callback to the looping function to help the async code look a bit more like the sync version. The idea is that by using a return callback, the loop
function doesn't need to know what function called it and where it should jump to after its done its job - in the end, calling onDone(x)
is a bit similar to doing return x
. Of course, you could have hardcoded the return function if you wanted.
function afterAjax(){
console.log("all done");
}
function loop(){
if(i >= urls.length){
afterAjax();
}
//...
}
loop();
Finally, coding recursive loops like this is a bit annoying and there are many libraries out there that provide functions to encapsulate these hight level sequentialization and parallelization patterns. In particular, error handling (try-catch) is specially hard to do by hand with callbacks. If you are doing any more non-tricial Async stuff I would highly recommend looking into some of these libraries.