JS visibility of a variable in function - javascript

I work with JSON and I want to count the number of elements in the response.
$.getJSON("/api/getEvents", function(data) {
$.each(data, function(key, event) {
var count = 10;
$.getJSON("/api/getUsers", function(data) {
$.each(data, function(key, event) {
alert("Value: " + count);
count++;
});
});
alert("Count: " + count);
});
});
As a result, I get:
Value: 10
Value: 11
Value: 12
...
Count: 10
Why count = 10?

It's because ajax requests are asynchronous. $.getJSON just initiates a request, but javascript execution immediately continues. You can see the count if you move the alert inside the ajax callback:
$.getJSON("/api/getEvents", function(data) {
$.each(data, function(key, event) {
var count = 10;
$.getJSON("/api/getUsers", function(data) {
$.each(data, function(key, event) {
alert("Value: " + count);
count++;
});
// I moved this here:
alert("Count: " + count);
});
// It used to be here.
});
});
So after you set var count = 10, the javascript parser then runs $.getJSON, but then immediately goes on to the next line, which in your code example alerted the "Count: 10". Then, whenever the request finishes, it runs the callback code that increments the count and alerts the Value lines.

Related

While looping a jquery ajax call

I want to control the number of ajax calls to a controller using a while loop.
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
(function () {
$.ajax({
url: '/algorithm',
method: 'GET',
data: $('#filter-form').serialize() + "&counter=" + counter,
success: function (data) {
alert("The data is " + data);
setCounter(parseInt(data))
},
error: function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
})();
}
alert("counter end = " + counter)
});
function setCounter(data) {
counter = data
}
Controller:
#RequestMapping(value = "/algorithm")
#ResponseBody
public String test(#RequestParam Map<String, String> allRequestParam) {
int counter = Integer.parseInt(allRequestParam.get("counter"));
counter++;
return Integer.toString(counter);
}
The controller basically just increments the counter and returns it and in the ajax success: it will set the global counter to that number.
When I do this, the page just freezes and I cannot click anything. I put the ajax call in a function for scoping but it still does not work. When I use a for loop, it seems the ajax does not invoke because I do not get any success or error alerts.
It doesn't work for a simple reason: the $.ajax call is asynchronous.
Take this example:
$(function() {
var t = 1;
console.log("Hey, the ajax will start! t's value: " + t);
$.ajax({
url: 'www.google.com.br',
method: 'GET',
success: function (data) {
t++;
console.log("We've received an answer! t's (incremented) value: " + t);
},
error: function (xhr, status, error) {
t++;
console.log("We've received an error! t's (incremented) value: " + t);
}
});
console.log("Hey, the ajax just ended.... Not really. t's value: " + t);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The output is:
Hey, the ajax will start! t's value: 1
Hey, the ajax just ended.... Not really. t's value: 1
We've received an error! t's (incremented) value: 2
That's because the $.ajax call is nonblocking, thus is doesn't block the program until it is finished, allowing the program to keep on executing the next line of code and continue running the ajax task in the background.
It is a recurrent issue in SO, so instead of providing solutions again here I'll ask you to read more on the questions:
How do I return the response from an asynchronous call?
How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
What does Asynchronous means in Ajax?
while will block synchronously until its condition is reached. Even if responses come back, the response will be asynchronous; the current thread (the while loop) will keep blocking forever.
Don't block. I don't see any reason to use a loop in the first place - instead, simply test to see if the counter is greater than the allowed number, and if it is, return:
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
if (counter >= 10) return;
If you wanted to make multiple requests in parallel on form submit, you could do that, but you would have to keep track of the counter client-side:
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
counter++;
// ... make request
As others have said your problem is that the call is asynchronous. This simple example may give you some idea about how to control the flow. It should be simple enough to apply it to your case.
I am simulating what you need to make your code work. For the errors, I am passing back null but you should bubble up any errors that may occur and either halt execution or deal with them some other way.
var count = 0; // used to store your count
// This represents the function you are
// waiting on with your ajax calls
function waitOne(num, callback) {
setTimeout(() => {
callback(null, num);
}, 1000);
}
// This represents your ajax call
function callWaitOne(callback) {
waitOne(count, (err, num) => {
// Your result is here
console.log(num);
// Callback to let the control function
// know the ajax has returned
callback(null);
});
}
// This will control the calls
function printWaitOne() {
callWaitOne((err) => {
if (count < 10) {
count++;
// Only calls if its callback
// has been called.
printWaitOne();
}
});
}
printWaitOne();

More JQuery/Ajax and when/done/promise confusion

Once again I struggle with ajax calls - this time around some chaining issue. Overall here is what I need to accomplish:
I loop over some array, and for each item in the array, I need to do the following:
Issue an Ajax call, and upon success, I need to issue three other calls, which must be chained, so they run in sequence.
When all the items in the array have both their main call and the three chained subcalls completed, I must be able to do some action.
My problem is, that the program does not wait for the three chained subcalls to complete. In the code below, this can be seen by the "Done" statement in the log turns up before the subcalls have completed.
I have created a JSFiddle here: https://jsfiddle.net/LeifFrederiksen/td534phz/1/
Note: I have two different function for the addAttachments function (addAttachments and addAttachmentsAlternative) - none of them works like they should.
var items = ["A","B"];
save();
function doneSaving() {
log("<H1>Done</H1>");
}
function save() {
// Save all items, and do something when all is done...
log("<H1>Save initiated</H1>");
var returnValue = saveItems();
$.when(returnValue).done(function() {
doneSaving();
})
}
function saveItems() {
// Loop through all items and save each of them...
var requests = Array();
// Build array of requests to wait for...
for (item of items) {
requests.push(saveOneItem(item));
}
var returnValue = $.when.apply($, requests).done(function() {
log("All requests completed");
})
return returnValue;
}
function saveOneItem(item) {
// Save one item...
return addListItem(item,addListItemSuccess,addListItemFailure);
}
function addListItem(item, successFunction, failureFunction) {
// The actual ajax that handles saving to database (actually Sharepoint via REST)...
log("addListItem on: " + item);
var returnValue =
$.ajax({
url: "/echo/json/",
data: {html: item,
delay: 1},
}).done(function (data) {
if (successFunction != undefined) {
returnValue = successFunction(item, data); // Returns the newly created list item information
return returnValue;
}
}).fail(function (data) {
return failureFunction(item, data);
});
return returnValue;
}
function addListItemSuccess(item,data) {
log("addListItem succces - in succes function for " + item);
returnValue = addAttachmentsAlternative(item,data);
return returnValue;
}
function addAttachments(item,data) {
var attachment1Deferred = addListItem(item + "-attachment 1",addAttachmentSuccess,addAttachmentFailure);
var attachment2Deferred = attachment1Deferred.then(
function() {
return addListItem(item + "-attachment 2",addAttachmentSuccess,addAttachmentFailure);
});
var attachment3Deferred = attachment2Deferred.then(
function() {
return addListItem(item + "-attachment 3",addAttachmentSuccess,addAttachmentFailure);
});
attachment3Deferred.done(
function() {
log("Completed upload of all attachments for " + item);
})
return attachment3Deferred;
}
function addAttachmentsAlternative(item,data) {
return addListItem(item + "-attachment 1",addAttachmentSuccess,addAttachmentFailure)
.done(function(data) {
return addListItem(item + "-attachment 2",addAttachmentSuccess,addAttachmentFailure)
}).done(function(data) {
return addListItem(item + "-attachment 3",addAttachmentSuccess,addAttachmentFailure)
}).done(function(data) {
log("Completed alternative upload of all attachments for " + item);
});
}
function addAttachmentSuccess(item,data) {
log("addAttachment succces - in succes function for " + item);
var deferred = $.Deferred();
deferred.resolve();
return deferred;
}
function addListItemFailure(item,data) {
console.log("addListItem failed - calling failure function for " + item);
$("#console").append("<P>addListItem failed - in failure function for " + item);
}
function addAttachmentFailure(item,data) {
console.log("addListItem failed - calling failure function for " + item);
$("#console").append("<P>addListItem failed - in failure function for " + item);
}
function log(message) {
console.log(message);
$("#console").append("<P>" + message);
}
I am hoping to achieve some generic pattern that I can use in different cases.
I got my inspiration from this great article, but cannot get it to work in my scenario: https://medium.com/coding-design/writing-better-ajax-8ee4a7fb95f#.tu0sruz5k
Any ideas and inputs are more than welcome.
Regards
Leif
There are several issues with the provided example:
to chain the tasks of creating list items and adding attachments use
.then instead of .done. With .done callback that prints All requests completed it is fired once deferred (first ajax call in addListItem function) is getting resolved.
some functions like addListItem still uses callback function syntax, i would suggest convert them to promises
since all deferred are getting resolved in saveItems function there is no need to use jQuery.when() in save function
Modified demo

Ajax call inside of loop needs to be synchronized

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.

force queue in .each() (javascript) [duplicate]

This question already has answers here:
Queue AJAX calls
(2 answers)
Closed 3 years ago.
How can synchrony be forced in a javascript loop?
This is without synchrony :
$.each(data, function(index, value) {
alert(index + ': ' + value);
});
synchrony attempt :
var time = 500;
$.each(data, function(index, value) {
setTimeout(alert(index + ': ' + value), time);
time += 500;
});
but so far all it does is run the alert in a row, without interval
This is the ajax request that I need to run in a QUEUE :
$.each(data, function(key, val) {
$.ajax({
url: 'ajax/getPerson.php',
type:'post',
dataType: 'json',
data: { 'dude' : val["idG"] },
success: function(data) { createHTML.person(data) }
}
Try this:
var time = 500;
$.each(data, function(index, value) {
setTimeout(function() {
alert(index + ': ' + value);
}, time);
time += 500;
});
Pass the code you want to execute in a function() object as first argument to setTimeout(), like so:
var time = 500;
$.each(data, function(index, value) {
setTimeout(function() { alert(index + ': ' + value) }, time);
time += 500;
});
According to your comment:
I am using this function to make a DataBase Requests and they are not coming back in the order I ask for
The callback method is intuitive and rather easier to implement, but what if you have 100 request to be made? Assuming 200ms per response-time that's total of 20 second.
So here is the question: Do you really care about the returning order of AJAX calls or you just have to process the returning data by the order of calls you have fired?
If the answer is latter, the solution will be:
fire all ajax calls at once ( or in queue )
maintain an array of returning datas
check array whether all requests have been successfully returned, re-request if there's an error ( or other error handling mechanism)
when all requests are returned, do the data processing you originally intended

Javascript: Arrays

For some reason my values are not being stored in the array:
var req = new Array();
$.get('./ajax/get_cat_info.php?cid=' +cid, function(data, textStatus) {
var count = 0;
$.each(data, function(key, val) {
$('#' + key).show();
if(val == 1) {
req[count] = key;
count = count + 1;
//var arLen=req.length;
//alert('l: ' + arLen); // this works though
}
});
}, 'json');
var arLen=req.length;
alert('l: ' + arLen);
I get alerted "l: 0" at the end. If I uncomment the line alert in the IF statement, it alerts on each one, then still alerts 0.
AJAX requests are, by default, asynchronous. You'll either have to change the AJAX request to be synchronous, or use the value of req in the callback.
In addition, you might want to use req.push(key) rather than using a count variable and req[count] = key; (although this isn't your problem).
The get call is running asynchronously, and so arLen=req.length is being evaluated prior to the function of elements being set actually completing. You can set the values accordingly from within the callback of the async call, as you determined.

Categories

Resources