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.
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 use Delphi XE7. When my Javascript calls my server function that need around 800ms to read sensor and return data, The browser is unresponsive from the moment I click the button to invoke the Javascript until it finally response returns. I'm using the default Javascript generated by the proxy var serverMethods().getChannel(i); to call into my server function.
Javascript call look like this:
var s = serverMethods().getChannel(i);
serial[i].$sensorlValue.text(s.result.fields.sensorString);
serial[i].$sensorlRealValue.text(s.result.fields.sensor);
serial[i].$sensorStatus.text(s.result.fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(s.result.fields.sensorError);
serial[i].$AVString.text(s.result.fields.AVString);
serial[i].$AVError.text(s.result.fields.AVError);
So by default example there are no Javascript callbacks or promise, so embaracaderom manage somehow to block Javascript from executing until response is back and variable a receive values?
I think about try using jQuery Ajax call on URL, but is there any other solution?
Because serverMethods are generated from proxy but for $ajax I need to manually set each of them. Or maybe I do something wrong here and serverMethods can be used without blocking ?
Thanks.
I found the solution to this problem after researching execution path in ServerFunctionExecutor.js that is called on serverMethods().SOMEAPIFUNCTION()
1. Help and documentation are 0, and google + XE7 questions are 0. So if someone from embaracadero read this PLS MAKE DECENT DOCUMENTATION.
ServerFunctionExecutor.js had on line 263
//async is only true if there is a callback that can be notified on completion
var useCallback = (callback != null);
request.open(requestType, url, useCallback);
if (useCallback)
{
request.onreadystatechange = function() {
if (request.readyState == 4)
{
//the callback will be notified the execution finished even if there is no expected result
JSONResult = hasResult ? parseHTTPResponse(request) : null;
callback(JSONResult, request.status, owner);
}
};
}
So it is posible and NOT DOCUMENTED to use callback for unblocking GUI.
Use it as:
serverMethods().SOMEAPIFUNCTION(par1,par2,.... callback)
If you have Server method defined in delphi code with for example 3 parameters in js 4th parameter is callback:
For this example code now look like this:
serverMethods().getChannel(i,function(a,b,c){
serial.$sensorlValue.text(a.result[0].fields.sensorString);
serial.$sensorlRealValue.text(a.result[0].fields.sensor);
serial.$sensorStatus.text(a.result[0].fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(a.result[0].fields.sensorError);
serial[i].$AVString.text(a.result[0].fields.AVString);
serial[i].$AVError.text(a.result[0].fields.AVError);
});
a is JSON reponse
b is Request status as number 200 or somethin else
c is owner usuali undefined
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 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.
I'm creating a script that performs several functions and I want to update the user as the functions are completed. I have nested $.ajax() calls with each subsequent call in the previous call's success block.
There are a total of 4 calls made for each loop. Let's call them scan_1 through scan_4. The success block of scan_1 calls scan_2 and so on down the chain.
For example, let's say I'm looping over 3 objects. I want the process to go like this:
Loop 1
scan_1
scan_2
scan_3
scan_4
Loop 2
scan_1
scan_2
scan_3
scan_4
Loop 3
scan_1
scan_2
scan_3
scan_4
The problem is that it's running through all the scan_1 calls first. I must be missing something, but I can't seem to figure it out. Any advice would be much appreciated.
For reference, here is a snippet of scan_1 (irrelevant stuff snipped):
for(var i = 1; i <= 3; i++)
{
$.ajax({
type: 'GET',
url: url,
data: 'do=scan&step=1&' + string,
dataType: 'json',
success: function (result)
{
if(result.proceed == 'true')
{
$('#scan_progress').append(result.message);
scan_2();
}
else
{
$('#scan_progress').append(result.message);
}
}
});
}
Thoughts?
Thanks in advance.
Sounds like you need to use jQuery deferred. It basically allows you to chain multiple event handlers to the jQuery Ajax object and gives you finer control over when the callbacks are invoked.
Further reading:
http://msdn.microsoft.com/en-us/scriptjunkie/gg723713
http://www.erichynds.com/jquery/using-deferreds-in-jquery/
It's asynchronous - the "success" fires sometime in the future. The script does not wait for it to respond. Since you're firing off three requests in your loop, they will all be "scan1".
"scan_2" will be called as each request completes.
Change the request to synchronous if you want to control the order of events.
You are starting by sending off three ajax calls at once.
Scan1 (loop 1)
Scan1 (loop 2)
Scan1 (loop 3)
When each Scan 1 completes, it's subsequent Scan 2, and then Scan 3 are called.
What did you actually want to happen? Scan 1 2 and 3 of loop 1, then 1 2 and 3 of loop 2, and then 1 2 and 3 of loop 3? That would require more nesting, or possibly deferred objects.
Instead of using the success callback for each $.ajax() call, you can store each set of AJAX requests (their jqXHR objects) in an array and wait for all of them to resolve:
function scan_1 () {
//setup array to store jqXHR objects (deferred objects)
var jqXHRs = [];
for(var i = 1; i <= 3; i++)
{
//push a new index onto the array, `$.ajax()` returns an object that will resolve when the response is returned
jqXHRs[jqXHRs.length] = $.ajax({
type: 'GET',
url: url,
data: 'do=scan&step=1&' + string,
dataType: 'json'
});
}
//wait for all four of the AJAX requests to resolve before running `scan_2()`
$.when(jqXHRs).then(function () {
if(result.proceed == 'true') {
scan_2();
}
});
}
I've had similar problems working heavily with SharePoint web services - you often need to pull data from multiple sources to generate input for a single process.
To solve it I embedded this kind of functionality into my AJAX abstraction library. You can easily define a request which will trigger a set of handlers when complete. However each request can be defined with multiple http calls. Here's the component (and detailed documentation):
DPAJAX at DepressedPress.com
This simple example creates one request with three calls and then passes that information, in the call order, to a single handler:
// The handler function
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) };
// Create the pool
myPool = DP_AJAX.createPool();
// Create the request
myRequest = DP_AJAX.createRequest(AddUp);
// Add the calls to the request
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]);
// Add the request to the pool
myPool.addRequest(myRequest);
Note that unlike many of the other solutions provided this method does not force single threading of the calls being made - each will still run as quickly (or as slowly) as the environment allows but the single handler will only be called when all are complete. It also supports the setting of timeout values and retry attempts if your service is a little flakey.
I've found it insanely useful (and incredibly simple to understand from a code perspective). No more chaining, no more counting calls and saving output. Just "set it and forget it".