jquery ajax async false causes click event issue - javascript

I have a script that runs through a multi-level array and each time calls a new ajax GET command to a php file with part of that array as the data.
Pretty basic...
for(var x=0; x<cities.length; x++){
for(var u=0; u<links.length; u++){
$.ajax({
url: "dontneedtoknow.php?city=" + cities[x] + "&link=" + links[u],
type: 'GET',
async: false,
cache: false,
timeout: 30000,
error: function(){
return true;
},
success: function(data){
//just appending data to page
}
});
}
}
I'd like to be able to have click() events and the ability to STOP this for loop but when this loop is going I can't do ANYTHING because of the async false.
I need the async false because I want the data to be appended as each function completes for a reason.
I have tried .live() but that doesn't seem to work...
Ideas?

When async is false, the entire browser* will be hung. You cannot do anything during a synchronous Ajax call other than waiting for the call to finish.
If you want to be able to stop the loop, you must use asynchronous calls.
See also:
What does "async: false" do in jQuery.ajax()?
IE7 hangs when using (to much) ajax calls with async: false
How to make all AJAX calls sequential?
That last link especially might be useful (if I understand what you're trying to accomplish here).
*unless you're in Chrome (then it's just the current page)

Why make that many calls to the server? That seems very inefficient to me.
Is there a reason you can not change the service you are calling to receive a list of items and return it? It would involve one Ajax call and the server side code can make sure the data is processed in order.

Related

Spinner button that starts spinning when function is called and stops when it ends

I'm trying to make a spinner button that will spin while I make an AJAX request and stop when the answer is received.
I've got the AJAX handled but the spinning doesn't seem to work with the following code:
function refresh (id){
var iconElem = document.getElementById("spinner" + id);
iconElem.classList.add('fa-spin');
sleep(5000);
var buttonRefresh = document.getElementById("refreshButton" + id);
buttonRefresh.classList.remove("fa-spin");
};
Note : I have replaced the ajax function with a sleep (implemented elsewhere, but it works like like it should) since I am in a non-php environment.
What happens here is that the the class "fa-spin" is being added while the sleep is over, even though it comes after in the code... Am I missing some kind of "refresh" that I need to execute in order to make the added class effective ?
You need to stop the spinning in the completion callback of the ajax call as it is a async call.
What you are doing here is starting and then immediately stopping the spinner before the ajax call even finishes.
$.ajax({
url: "test.html",
cache: false,
success: function(html){
// stop the spinner here
}
});
Here is the simplest solution with a callback:
function sleep(callback,timeout){
setTimeout(callback,timeout)
}
sleep(() => {
//stop spinner here
},200)
Anyways, I suggest you to read more here
If you are doing an ajax request, you can also use the async:false header to make your request synced, and then your code should work.
Changes to the style or content of the document become effective only when the JavaScript function finishes and returns to the main event loop. Therefore, assuming your sleep() function works as expected (by doing a busy wait or something like that, although that is not actually sleeping), you can only see the total effect of all changes when the function returns. If you follow the advice of the other answers and remove the style in the callback of the AJAX call, you will be fine.

How JQuery Ajax works?

I am trying to send data from JQuery Ajax to a Generic Handler that calculates something and returns a result. The Ajax request is made inside a for loop at JQuery end. The code looks something like this:
function send(Handler, ids) {
var URL = "http://" + window.location.host + "/Handlers/" + Handler + ".ashx";
var i;
for (i = 0; i < 10; i++) {
var cur = $('.' + ids[i]);
$.ajax({
url: URL,
type: "POST",
data: JSON.stringify({
Data: cur
}),
dataType: "json",
cache: false,
beforeSend: //my before send code,
success: //my success code,
error: //my error code
});
}
alert('Done!');
}
I placed 3 breakpoint in Visual Studio 2012 at line:
$.ajax({
this
alert('Done!');
And third breakpoint at first line in the Generic Handler.
Now, when I try to execute this code, the Ajax works nicely in async way. But, when it reaches the first breakpoint, stops there and then I resume it, instead of reaching the Generic Handler's breakpoint, it continues the loop and goes back to first breakpoint. Then after it reaches the second breakpoint it then stops at generic handler's break point again and again for each value.
So, does that mean that Ajax first collects all the ajax requests and then after the for loop it executes them together?
Javascript is single threaded and non-blocking. This means that in the first iteration won't wait for the ajax call to be completed, it will go back and start the second iteration, and so on.
So, no it doesn't executes them all together. It definately starts the ajax calls in the order of the loop but there is no way to tell what will end first. It might make all the ajax calls and then get an answer (doesn't mean it is the answer of the first iteration), or in the middle of a loop it might be getting answers.
If I am understanding you correctly, you just answered your own question. Ajax is working asynchronously meaning the for loop starts and fires out ajax requests, and continues the loop (the ajax request DOES NOT block)
Therefore it is very likely that the js is performing a loop of code before the request reaches your url (as this has to create a network call)
That said, what are you doing in your beforeSend method? maybe this is making it take enough time that it can perform all iterations of the loop before sending the first request?
To answer your question, no it shouldn't be waiting for the for loop to finish in order to send off the requests, it should be initiating the process as soon as you have made the call

Detecting When Javascript is Done Executing

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.

$(window).unload not working as expected

I'm making a small chat application with PHP + MySQL + JavaScript, I've written a function disonnectUser(), which is called when the user press the disconnect button. Here it is:
function disconnectUser(){
$.post('web/WEB-INF/classes/handleChatUser.php',{ action: 'disconnect',nick: localNickname});
$('#chat').stop(true,true).fadeOut(2000,function(){
nicknameDialog();
});
$('#messageInput').val(null);
$('#clientList').html(null);
$('#chatScreen').html(null);
clearInterval(refreshIntervalId);
clearInterval(refreshIntervalId2);
connected = false;
}
And it works like a charm, but when I call this very function in another context, when the user instead of pressing disconnect just exit the page, in this function
$(window).unload(function() {
if(connected){
disconnectUser();
connected = false;
}
});
it doesn't work. And I'm sure it's being called, because if I put an alert it's called normally before closing the page. I think the page is closing before the code runs completely, so I think if I put some block there until the code finish running it would work?
The problem is that $(window).unload() doesn't waits any AJAX call before closing the window (what is right because AJAX is assync).
You need to force the AJAX to be sync, ie, wait the response. Inside your disconnectUser function:
$.ajax({
type: 'POST',
async: false, // This is the guy.
url: '/blablabla'
});
You can read more about it here: $(window).unload wait for AJAX call to finish before leaving a webpage
Instead of unload, how about beforeunload?
window.onbeforeunload = function() {
if(connected){
disconnectUser();
connected = false;
}
};
Also, your disconnectUser method already sets connected to false, no need to do it here also.
It also seems that jQuery doesn't really handle the beforeunload event, which is why you'll need to revert to native JS to handle this:
http://groups.google.com/group/jquery-en/browse_thread/thread/4e5b25fa1ff5e5ee?pli=1
Try using a synchronous request. Perhaps in combination with onbeforunload like the other poster suggested. If that doesn't work, I suppose you're out of luck. A request that is synchronous blocks the browser while it's happening, so you might want to use it only for the unload function, assuming the method even works.
function disconnectUser(){
jQuery.ajax({
url: 'web/WEB-INF/classes/handleChatUser.php',
data: { action: 'disconnect',nick: localNickname},
type: 'POST',
async: false
});
$('#chat').stop(true,true).fadeOut(2000,function(){
nicknameDialog();
});
$('#messageInput').val(null);
$('#clientList').html(null);
$('#chatScreen').html(null);
clearInterval(refreshIntervalId);
clearInterval(refreshIntervalId2);
connected = false;
}

jquery ajax synchronous call beforeSend

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();
});

Categories