I have a jQuery POST function inside of a loop (multiple post calls).
For some reason, jQuery is posting, but not returning all the data until the loop ends and parsing all the results at once.
FOREACH arg_value IN args
{
console.log('Do stuff');
$.post('https://blah.com/xml.php',
{arg: arg_value},
function(xml) {
$(xml).find('tag').each(function() {
console.log('Do more stuff');
});
});
}
The output of this is...
Do stuff
Do stuff
Do stuff
Do more stuff
Do more stuff
Do more stuff
It seems like jQuery is caching the result or not executing it until the end.
I'm expecting...
Do stuff
Do more stuff
Do stuff
Do more stuff
Do stuff
Do more stuff
Is there an option to tell jQuery to pause execution until there is a result from the AJAX? It appears to be running asynchronously as usual, but I don't want that in this case.
A AJAX call is asynchronous. This means your callback:
function(xml) {
$(xml).find('tag').each(function() {
console.log('Do more stuff');
});
}
Only gets executed when the server returns a response.
Meanwhile, your for-each loop will keep running, and logging Do stuff.
Unless you're doing crazy stuff in your loop, the loop will be faster than the server response
Like Jan Dvorak mentioned, Even if you do crazy stuff, the AJAX callbacks will wait for the loop to finish, resulting in Do stuff always appearing before any of the AJAX responses.
Your ajax call is asyncronous, which means that the ajax call is fired off, then the execution flow of further code continues. It does not wait around for the response. When the response is finally received, the callback is executed.
What you could do is to have your ajax calls all stack up in a queue and as each response is received, fire off the next call in the queue. This would achieve your diesired effect, whereby the next ajax call is not sent until the current one is done.
If you have many calls, this would be much slower than firing them all off as soon as they are needed because browsers can easily handle multiple ajax calls at the same time.
$.post() is async call...will not work in loops the way u want..To get the way u want..here is the solution..
var i=0;
callbackfunc(i) {
console.log('Do stuff');
$.post('https://blah.com/xml.php',
{arg: args[i]},
function(xml) {
$(xml).find('tag').each(function() {
//something related to xml data
});
console.log('Do more stuff');
i++;
if(i<args.length)callbackfunc(i)
}
});
}
take care of variables..may create closures here...
you are asynchronous here, if you want to do that probably the easiest way to achieve this is to use async.js
Related
In Javascript, I have two asychronous requests for data:
$.getJSON('http://foo.com', fooQuery, fooSuccess(data));
$.getJSON('http://bar.com', barQuery, barSuccess(data));
and two callbacks to process the received data:
fooSuccess(data) { // Stuff }
barSuccess(data) { // More Stuff }
How do I ensure barSuccess is executed only after fooSuccess completes?
Notes:
I want to keep the data requests as they are: asynchronous and non-blocking (since server responses may take a while).
But, I want the callbacks that process the data to be executed sequentially. That is, I do not want to execute barSuccess until fooSuccess completes.
Thanks so much for your wisdom and help!
Here is how you would do it using the jQuery deferred object that is returned by ajax requests.
var fooDfd = $.getJSON('http://foo.com', fooQuery);
var barDfd = $.getJSON('http://bar.com', barQuery);
fooDfd.then(function(fooData){
fooSuccess(fooData);
barDfd.then(barSuccess);
});
The best way would be to utilize the jQuery when().done() functionality like this:
$.when(
$.getJSON('http://foo.com', fooQuery, fooSuccess(data)),
$.getJSON('http://bar.com', barQuery, barSuccess(data))
).done(function(arg1, arg2){
fooSuccess(arg1);
barSuccess(arg2);
});
This allow simultaneous execution of the AJAX requests and guaranteed execution of the done() function once all requests has successfully completed.
I'm follow this very interesting post from a half an hour ago, when appear the elegant solution presented by #Mike Brant I quickly was to dive in the jquery library to see how the magic is made. Don't you? I recommend, is very interesting!
BTW I think we don't need all that magic, not in this case, we have two asynchronous calls handlers(functions), no matter which end first, we need to know when the second end, then all we need is a third function that will be called by the two handlers and act when all the data is ready. I know this approach will vaste four or five lines more of code than the elegant jquery solution, but at the end our brain and soul will be in better condition. Sorry my english.
Put the barSuccess call in the fooSuccess success callback.
fooSuccess(data){
jQuery.ajax({
data: data,
success: function(response){
barSuccess(data) //should still be in scope, I think?
}
}
}
But for some reason, when i put variable data into naughty, it does not have content of data.
function cookie() {
var naughty = 'aaa';
$.post('/cookie', function(data) {
naughty = data;
});
alert(naughty); // sends "aaa"
}
Why?
UPDATE: thank you! now i got it, its because $.post() is async, so alert(naughty) will be executed before it will be filled with data.
Now i have sub-question: when i turn off async, will only function cookie() postpone execution or whole browser engine will freeze until AJAX is done?
UPDATE 2: Yes, #Kevin B, you are right, naughty is never filled... i did mistake in first code sample. I am lucky that all answers are valid anyway with only second code sample :-)
As #Kevin B pointed out, async=false will be depreciated in jQuery 1.8. And it freezes browser until request is done...
sub-question #2: i have more code inside cookie(), after this AJAX request. So, can i somehow pause execution of cookie() untill AJAX is done, without freezing whole browser with async=false? Or do i have to find new way (using complete/success callback) of coding this idea?
LAST EDIT:
I realized that what i want is ASYNC:FALSE... So i simply used it. BTW. ASYNC:FALSE will NOT be removed from jQuery! Only special kind of use-case will be...
It's a race condition. As .post() is an asynchronous function, the alert executes before naughty gets reset to data.
If you want the response to come back before continuing, you can use .ajax and set async to false.
How can I stop $.post in jQuery working asynchronously?
Just do this:
function cookie() {
var naughty = 'aaa';
$.post('/cookie', function(data) {
naughty = data;
callNaughty(naughty);
});
}
function callNaughty(naughty) {
alert(naughty);
}
OR
function cookie() {
var naughty = 'aaa';
$.post('/cookie').done(function(data) {
naughty = data;
alert(naughty)
});
}
As $.post() is asynchronous, so naughty will update after response arrived.
Note
async: false is deprecated in jQuery 1.8
This is because $.post is asynchronous. Execution leaves that line immediately while page is being requested and function(data) is yet to be called so data is empty. The empty data is what alert picks up. This can only work after the request has completed e.g. in synchronous fashion in which the alert statement has to wait
Since $.post it's an ajax request, it's asynchronous by nature, so what you'd want to do it's to make sure that after you ajax request has been completed, then do whatever you want:
function cookie() {
var naughty ;
$.post('/cookie', function(data) {
naughty = data;
}).done(function(){
alert(naughty);
});
}
My guess would be that $.post is working asynchronously. So your alert is being called before the post callback has been executed. If you place the alert in the post callback you should get the correct result.
This code hangs.
I am reasonably sure it's because the response in the anonymous function is a new variable not connected with the outer scope. How do I solve this?
function foo() { //...
var url = "http://urliciously-urlish-url"
response = null;
$.get(url, function (data) {response = data;
});
while( response === null)
{
1;
}
console.log(response);
//...
}
Note I am aware that this design will (as usual for polling systems) hang the page until response becomes non-null. That's OK in this context.
$.get is asynchronous. If you really want this to be synchronous, you'll have to use the $.ajax function:
$.ajax({
url: url,
async: false,
success: function(data) {
response = data;
}
});
That being said, I agree with cHao -- you should get used to writing asynchronous code.
While your code is running, the event handlers won't.
Translation: This code won't work.
If you want to use JS, you'll almost definitely have to get used to writing asynchronous code.
No, the problem is that the callback will never be executed because the JS thread is still running. It will wait for the current context to be ended, before it goes on executing the "next tick" (the callback here). Between the ticks it may also update DOM and handle other things.
Instead of using a hanging loop, use a synchronous ajax request (yes, thats possible). In jQuery: {async:false}. However, it then will "only" hang until the request is ended - which maybe never happens. And unresponsable GUI is the worst thing to happen, so DO NOT USE it.
Is there an event in javascript that I could bind some sort of listener to that will tell me when all javascript/jQuery/Ajax is done executing on the page? The page will not be loading/unloading/reloading, etc between the time the execution begins and the time that I need the listener to "listen", so those events don't work. The page literally is not doing anything. The button is clicked and some javascript functions fire which contain Ajax calls to web services. After all have finished, I want to change window.location. But window.location is changing before the web services have finished in my case.
Currently using setTimeout to achieve this, but as sometimes the code needs more time to run than normal, sometimes the window.location is firing before all the other javascript has finished. Simply put
<input type = "button"... onclick="doThis();";
function doThis() {
try{
//Contains AJAX calls to web services which is mainly what screws up my timing since it may still be trying to execute stuff when the redirect statement happens
}
catch (e) {
}
//Currently doing setTimeout(redirect, 10000);
//Would like to simply detect when all of the above is done and then redirect.
}
Edit: Left out a crucial piece of info. The AJAX calls are in a for loop. The use of variables and success callbacks hasn't been working so well for me as by the time my success callback is executing, my variables have taken on new values in the for loop.
What you are trying to achieve is a classical concurrent programming problem. It is solved by the use of a barrier.
To put it simply, you need to:
Count how many calls you've done.
Set a callback on all AJAX completion events.
Make that callback decrement the number of calls.
The callback checks whether the number of calls has reached zero or not. If yes, then your final code (here, redirect) is called.
The actual implementation is left as an exercise to the reader :)
Hint: embed AJAX calls into a function that handles all counter incrementation and callback setting.
What I do:
Create a variable that represents the number of outstanding AJAX calls.
Before making an AJAX call, increment the variable.
At the end of the code that completes an AJAX call, call a function (e.g. ajaxComplete).
ajaxComplete should decrement the count. When it reaches zero, you know all your calls are complete.
Assuming you're using jQuery.ajax, it sounds like you're looking for ajaxStop.
Why don't you try using something like the Underscore library's after function in the callbacks?
var done = _.after(3, function() {
window.location = 'http://example.com';
});
$.ajax({
url: '/tic',
success: function() {
done();
}
});
$.ajax({
url: '/tac',
success: function() {
done();
}
});
$.ajax({
url: '/toe',
success: function( data ) {
done();
}
});
You should check for the response from AJAX call, and only in that response do redirect. This way you will avoid doing redirect while AJAX was still executing.
I have a function called:
function callAjax(url, data) {
$.ajax(
{
url: url, // same domain
data: data,
cache: false,
async: false, // use sync results
beforeSend: function() {
// show loading indicator
},
success: function() {
// remove loading indicator
}
}
);
}
In the code, I call "callAjax" X number of times and I want to update the data synchronously. It is done as expected, but one problem: the loading item doesn't show in beforeSend function. If I turn async to true, it works but the updates aren't synchronously done.
I've tried several things with no success. I tried putting the loading indicator before the ajax call like this:
function callAjax(url, data) {
// show loading div
$.ajax(
{
// same as above
}
);
}
But for some reason it doesn't want to show the loading indicator. I notice a strange behavior when I put an "alert" in the beforeSend and the loading indicator appears in that case, but I rather not pop up a message box.
Got any ideas?
Making a synchronous call like that is like putting up an "alert()" box. Some browsers stop what they're doing, completely, until the HTTP response is received.
Thus in your code, after your call to the "$.ajax()" function begins, nothing happens until the response is received, and the next thing as far as your code goes will be the "success" handler.
Generally, unless you're really confident in your server, it's a much better idea to use asynchronous calls. When you do it that way, the browser immediately returns to its work and simply listens in the background for the HTTP response. When the response arrives, your success handler will be invoked.
When you do the blocking I/O the program is halted until the the input is received, in JS words when doing a synchronous call, the program halts and browser window freezes (no painting can be done) until the response is received. In most cases doing syncronus calls and any kind of blocking I/O can be avoided. However imagine your doing a progress bar in java or any other programming language, you have to spawn a different thread to control the progress bar, I think.
One thing to try in your case, is to call the ajax call after a time delay
//loading div stuff,
//if your doing some animation here make sure to have Sufficient
//time for it. If its just a regular show then use a time delay of 100-200
setTimeout( ajaxCall, 500 );
EDIT ajaxcall in setTimeout, Example
This is what you are looking for - .ajaxStart()
It will be triggered when any ajax event starts
http://api.jquery.com/ajaxStart/
They even give a specific example similar to what you are trying to accomplish:
$("#loading").ajaxStart(function(){
$(this).show();
});
You can then use the .ajaxStop() function
$("#loading").ajaxStop(function(){
$(this).hide();
});