I may be doing something very silly here. Basically a user can have multiple "sites" and for each site we have to run some calculations to come up with a total. We have a php page which does these calculations which we can call calculate.php. It returns a string ex: 50 which we then parse into a float in JS. So here's what I'm trying to do: get the total of all of the numbers outputted by calculate.php.
My idea was to loop through the sites, $.post() to calculate.php within every iteration (other things are being done too in the loop, less important) and add to a variable in the callback function. I believe my problem is that $.post() is async... Here is my sample code:
function systems(siteList){
var runningTotal = 0;
for (var ii=0,i<siteList.length,i++){
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
})
}
//outside loop
alert(runningTotal)
}
This function may be imperfect, but my real question is how can I achieve the result I'm looking for here? Just so you know, runningTotal always alerts as 0 with the above code.
Thanks
EDIT: Thanks to all for your help. from what I can see, it is not wise of me to be using so many AJAX calls so instead of directly fixing this problem, I believe I will take a step back and take the advice of sending only one ajax callback to perform the task of summing for me. Thanks again
I'd suggest to make use of $.when and run a callback once all Ajax calls are done. At that moment you have access to the response of each Ajax call and you can perform any necessary computation.
For example:
function systems(siteList){
var promises = [];
for (var ii=0; i<siteList.length; i++){
// `$.post` returns a promise. We keep track of each promise.
promises.push($.post("calculate.php",{foo:siteList[ii]}));
}
$.when.apply($, promises).then(function() {
// this is called once the responses of all Ajax calls
// have been received
var sum = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
sum += +arguments[i][0]; // value must be cast to number first
}
alert(sum)
});
}
Have a look at the jQuery documentation to learn more about promises: http://learn.jquery.com/code-organization/deferreds/.
In general though, it's better to make as few Ajax requests as possible. If you can change the server side code, I would change it to accept a list of values, not only one value, and let the server return the final result.
I recommend you package up your data on the client side (in JavaScript) and send it to the server in one AJAX request. Multiple requests waste bandwidth and time.
Or, if the calculations are simple and don't need a database or some other external dependency, just move your calculations to a library of javascript functions and do it all client side.
Leaving aside that there should probably be one request doing this sort of summing up the calculations on the server side (IMHO), use a counter and when it reaches 0 you know, you have all results collected:
function systems(siteList){
var runningTotal = 0;
var counter = 0;
for (var ii=0,i<siteList.length,i++){
counter++;
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
counter--;
if(counter==0)
alert(runningTotal)
});
}
}
Not very fancy, but you could use a file on the server to store your running total.
As each ajax request is received, read/update total/write to a file on the server.
Then send that value back either after receiving a specific request (&request=send_total), or somehow appended to the output for each request ( 50|3753 ).
AJAX is asynchronous.
Do the alert inside of the AJAX callback.
Related
SOLVED: I solved my problem by doing each XMLHttpRequiest() recursively. Basically at the end of my xhr.onload, I would make another request and actively check if I've reach the end of my data - when I have I return.
I'm fairly new in JavaScript and have some familiarity with the D3 Library. I'm trying to read a CSV file from my computer using the D3 Library and sending specific information from my file to an API through an XMLHttpRequest().
With each call to the API which returns a JSON object to me, I store that object in a dataset for later use. I'm trying to have it so that my whole CSV file is read and processed before I work with the dataset, however I'm running into a problem since the API calls are asynchronous.
My code looks something like this:
var myData = [];
d3.csv("myFile.csv", function(data)
{
for (var i = 0; i < data.length; i++)
// Get appropriate data from data object
// Make API call with XMLHttpRequest() and store in myData array
});
// Handle fully updated myData array here
As it is, my code currently goes through my loop in almost an instant and makes all the API calls asynchronously and then proceeds to work on my data without waiting for anything to update.
Is there a way to ensure that my CSV file has been processed and all the API calls have returned before I can work with this dataset? I've tried callback functions and promises but had no success.
You can easily do this with a simple counter
var counter = 0;
var myData = [];
d3.csv("myFile.csv", function(data)
{
for (var i = 0; i < data.length; i++){
// Get appropriate data from data object
$.get("your/api/path", function(result){
counter++; // this is important. It increments on each xhr call made.
myData.push(result);
if(counter === data.length) cb(myData); // This will check if the current xhr request is the last xhr request. If yes then call our cb function.
});
}
});
function cb(data){
// This will run only when all the http requests are complete
// Do something with data
}
All this code does is, it makes sure that all of our requests should be completed first before calling our cb function (here you will write your further logic). This approach guarantees that cb will run only when all xhr requests are completed.
I think the answer in this post could help
d3: make the d3.csv function syncronous
You can as well use the Promise API.
I am trying to send a xmlHttpRequest in a while loop and want to do something with the response in the same while loop. Since the requests are asynchronous, how can I achieve it? I need to execute everything serially
while(i < n){
response = self.sendHttpRequest(params);
//do something with the response
}
Any help would be appreciated.
Even If I use a callback, how can I get back to same while loop after executing the callback?
There are two ways I can think of:
1. Add a polling loop after the get call that waits until the response.readyState is set and then process the response:
while(i < n){
response = self.sendHttpRequest(params);
while( response.readyState != 4 ){
// polling wait
}
//do something with the response
}
This option is not really recommended since it stops the flow of the code and you can get stop in the loop if the readyState never changes (not likely, but possible with errors).
2. You can encapsulate the request in a function that will be called recursively when the last response handling finishes:
var i = 0;
function handle( response ){
//handle response
i++;
if( i < n ) sendRequest();
}
function sendRequest(){
// Your request setup code
response.onreadystatechange = handle;
response = self.sendHttpRequest(params);
}
The second method is preferred in my opinion, as it maintains the asynchronicity of the html request call, and doesn't stop the flow of the code, however it does "break" the loop structure. The first method keeps the loop structure, but is not very good coding practice.
Are you using any ajax library or plain js. If you are not using any library ,you can pass third argument to open method false.like below
var xmlHttp=new xmlHttpRequest();
xmlHttp.open({YOUR_METHOD},{YOUR_PATH},false);
Passing false to open method makes synchronous call .so you can handle the return in same loop.
I have an ajax call that retrieves data and on the success of it, runs a loop and runs functions that run more ajax calls.
CODE:
success: function(data){
// FIRST MAKE SURE DATA WAS FOUND
console.log(data);
if(data["status"] == "found")
{
// CREATE NEEDED ARRAY FROM SINGLE STRING
var restrictionArray = data["data_retrieved"].split(',');
loop_amount = restrictionArray.length; //<!-- AMOUNT TO BE LOOPED FOR BUILDING FORMS
//var Action = this.Elevation.getActionsByOptionId(Option.getID())[i];
for(var j = 0; j < loop_amount; j++)
{
var EditRowRestriction = OptionRules.prototype.getEditRowRestriction(j);
var check = $(EditRowRestriction).find("select");
console.log(check[0]);
var EditRowRestirction_select_ability = OptionRules.prototype.getEditRowRestriction_select_ability(j);
//var EditRowRestirction_access_ability = OptionRules.prototype.getEditRowRestriction_access_ability(j);
EditRowRestriction.onremove = function()
{
$(this).next().empty(); <!-- RESTRICTION SELECT ABILITY REMOVE
//$(this).next().next().empty(); <!-- RESTRICTION ACCESS ABILITY REMOVE
$(this).empty();
//var Action = this.Action;
//that.removeAction(Action);
}
tbody.appendChild(EditRowRestriction);
tbody.appendChild(EditRowRestirction_select_ability);
console.log(check[1]);
}
}
},
error:function(){
alert("An error occured, please try again.");
}
Heres the problem, inside the for loop; those methods link to another method that invokes an ajax call. What happens here is even before the ajax call's finish, the loop is always continuing and those methods are always being called. What I want to do is to stop the loop until those methods have returned based on the ajax call's being finished. And than to invoke the last 2 lines of code within the loop:
tbody.appendChild(EditRowRestriction);
tbody.appendChild(EditRowRestirction_select_ability);
What would be my best approach to accomplishing this?
Suggestions, thoughts?
It would be best to consolidate all of this looping with a single server-side script, however, if that isn't an option, you can use .then:
var def = $.Deferred(function(def){
def.resolve();
}).promise(); // used to start .then chain
for (var i = 0; i < 10; i++) {
def = def.then(function () {
return $.ajax({});
});
}
def.done(function(){
// All requests from chain are done
console.log('all done');
});
http://jsfiddle.net/kg45U/
You could modify the calls to $.ajax within the loop to execute synchronously, rather than asynchronously. (See SO question How can I get jQuery to perform a synchronous, rather than asynchronous, AJAX request?.) This would have the effect of "pausing" the loop while the inner ajax calls execute. This would be the most straightforward approach, but does have the disadvantage of locking up the user's browser while those ajax requests execute.
The alternative is to break up the code with the loops into pieces, so that you complete the processing with the loop in a callback function that is invoked after the inner-ajax calls have completed.
I'm new in the scripting and web world and have been trying to work through an issue I've been having. I am reading data from a local JSON file, and have been able to use jQuery.getJSON and jQuery.parseJSON successfully, but I am trying to use the data outside of the getJSON callback function and am having issues. I think it comes down to me not fully understanding the correct way to do this, and that's where I'm looking for your help. Here's my code:
var names = new Array();
$.getJSON('ferries.json', function(data) {
var jsondata = $.parseJSON(JSON.stringify(data));
var length = jsondata.nodes.length;
for (var i = 0; i < length; i++) {
names[i] = String(jsondata.nodes[i].name);
}
});
console.log('Names: ' + names[0]);
The final line returns undefined. If I were to write that line right after the for loop, it would return the desired value. Here's how the JSON file is structured:
{
"nodes":[
{
"name":"John"
},
...
{
"name":"Joe"
}
]
}
Any help would be appreciated - thanks!
Edit: One last thing, it seems that the final line (console.log(...)) executes before the $.getJSON bit, which confuses me as well.
$.getJSON runs asynchronously. The function that you pass to it is a "callback", which means that it gets called when getJSON comes back from doing its thing.
If you want to do something with the JSON data that you get back, you must wait for the callback to execute.
Also, on a side note, $.parseJSON(JSON.stringify(data)) is redundant. The data object is already a perfectly usable object with your data in it, but you're turning that object back into a JSON string and then immediately back into an object. Just use data as is. For more information, check out the jQuery API docs for getJSON.
I want to retrieve the height and width of an image on a server by using an ajax post call to a php file which returns a double pipe delimited string 'width||height'
My javascript is correctly alerting that requested string from the php file so the info is now in my script but i cannot seem to access it outside the $.post function.
This works:
var getImagesize = function(sFilename)
{
$.post("getImagesize.php", { filename: sFilename, time: "2pm" },
function(data){
alert(data.split('||'));
});
}
But retrieving is a different matter:
// this line calls the function in a loop through images:
var aOrgdimensions = getImagesize($(this, x).attr('src')) ;
alert(aOrgdimension);
// the called function now looks like this:
var getImagesize = function(sFilename)
{
var aImagedims = new Array();
$.post("getImagesize.php", { filename: sFilename },
function(data){
aImagedims = data.split('||');
});
return "here it is" + aImagedims ;
}
Anyone able to tell me what i'm doing wrong?
You are misunderstanding the way that an AJAX call works. The first "A" in AJAX stands for asynchronous, which means that a request is made independent of the code thread you are running. That is the reason that callbacks are so big when it comes to AJAX, as you don't know when something is done until it is done. Your code, in the meantime, happily continues on.
In your code, you are trying to assign a variable, aOrgdimensions a value that you will not know until the request is done. There are two solutions to this:
Modify your logic to reconcile the concept of callbacks and perform your actions once the request is done with.
Less preferably, make your request synchronous. This means the code and page will "hang" at the point of the request and only proceed once it is over. This is done by adding async: false to the jQuery options.
Thanx for the Asynchronous explaination. I did not realize that, but at least now i know why my vars aren't available.
Edit: Figured it out. Used the callback function as suggested, and all is well. :D