Page waits for AJAX before changing location - javascript

This question might seem a bit odd, the problem arised when the page went through webtests.
The page uses an AJAX call (async set to true) to gather some data. For some reason it won't swap pages before the AJAX call has returned - consider the following code:
console.log("firing ajax call");
$.ajax({
type: "POST",
url: "requestedService",
data: mode : "requestedMethod",
cache: false,
dataType: "json",
success: function() { console.log("ajax response received") },
error: null,
complete: null,
});
console.log("changing window location");
window.location = "http://www.google.com"
The location only changes after AJAX returns the response. I have tested the call, it is in fact asynchronous, the page isn't blocked. It should just load the new page even if the AJAX call hasn't completed, but doesn't. I can see the page is trying to load, but it only happens once I get the response. Any ideas?
The console output is:
firing ajax call
changing window location
ajax response received

This seems to work fine for me. The location is changed before the code in the async handler executes. Maybe you should post some real code and not a simplified version, so that we can help better.
Here is a demonstration that works as you expect: http://jsfiddle.net/BSg9P/
$(document).ready(function() {
var result;
$("#btn").on('click', function(sender, args) {
setInterval(function() {
result = "some result";
console.log("Just returned a result");
}, 5000);
window.location = "http://www.google.com";
});
});
And here is a screenshot of the result: http://screencast.com/t/VbxMCxxyIbB
I have clicked the button 2 times, and you can see in the JS console that the message about the location change is printed before the result each time. (The error is related to CORS, if it was the same domain, it would navigate).

Bit late but maybe someone else will have the same issue.
This answer by #todd-menier might help: https://stackoverflow.com/questions/941889#answer-970843
So the issue might be server-side. For eg, if you're using PHP sessions by default the user's session will be locked while the server is processing the ajax request, so the next request to the new page won't be able to be processed by the server until the ajax has completed and released the lock. You can release the lock early if your ajax processing code doesn't need it so the next page load can happen simultaneously.

Related

Ajax file upload returns status code 0 ready state 0 (only sometimes)

I have looked at the following thread
jQuery Ajax - Status Code 0?
However I could not find a definitive answer and I am having serious trouble trying to find the source of my issue so I am posting here in the hopes that someone can point me in the right direction.
In my code I am performing an Angular HTTP post which just sends basic JSON data, then within the on success callback I am using AJAX to upload files to the same server. (I know I should not be using jQuery and Angular however I can't change this for the moment)
It looks something like this
var deferred = $q.defer()
// first post
$http.post(url,payload,{params: params, headers: headers)
.then(function(response) {
uploadFiles(response,deferred);
// I am also sending google analytics events here
}, function(error) {
// do error stuff
}
return deferred.promise;
// upload files function
function uploadFiles(response,deferred){
$ajax({
type: 'POST',
processData: false,
contentType: false,
data: data // this new FormData() with files appended to it,
url: 'the-endpoint-for-the-upload',
dataType: 'json',
success: function(data) {
// do success stuff here
deferred.resolve(data);
},
error: function(jqXHR, textStatus, errorThrown) {
var message = {};
if (jqXHR.status === 0) {
message.jqXHRStatusIsZero = "true";
}
if (jqXHR.readyState === 0) {
message.jqXHRReadyStateIsZero = "true";
}
if (jqXHR.status === '') {
message.jqXHRStatusIsEmptyString = "true";
}
if (jqXHR.status) {
message.jqXHRStatus = jqXHR.status;
}
if (jqXHR.readyState) {
message.jqXHRReadyState = jqXHR.readyState;
}
if (jqXHR.responseText) {
message.jqXHR = jqXHR.responseText;
}
if (textStatus) {
message.textStatus = textStatus;
}
if (errorThrown) {
message.errorThrown = errorThrown;
}
message.error = 'HTTP file upload failed';
logError(message);
deferred.resolve(message);
}
}
})
}
Not my exact code but almost the exact same.
The issue is that is works almost all of the time, but maybe three or four in every few hundred will fail. By fail I mean the error handler function is called on the file upload function and the files are not uploaded.
I get jqXHRStatus 0 and jqXHRReadyState 0 when this occurs.
The only way I am able to replicate the issue is by hitting the refresh on the browser when the request is being processed, however users have advised they are not doing this (although have to 100% confirm this)
Is there perhaps a serious flaw in my code which I am not seeing? Maybe passing deferred variable around isn't good practice? Or another way the ajax request is being cancelled that I am not considering? Could sending google analytics events at the same time be interfering?
Any advice would be great and please let me know if you would like more information on the issue.
This means, the request has been canceled.
There could be many different reasons for that, but be aware: this could be also due to a browser bug or issue - so i believe (IMHO) there is no way to prevent this kind of issues.
Think for example, you get a 503 (Service Unavailable) response. What you would do in such a case? This is also a sporadic and not predictable issue. Just live with that, and try to repost your data.
Without reinventing the wheel, I suggest you to implement:
Retrying ajax calls using the deferred api
My guess is that your code is executing before it actually gets back from the call. I.e. the call goes back and nothing was returned and it gives a 0 error. This would make sense as the error is variable. Most of the time it would return fine because the backend executed fast enough but sometimes it wouldn't because it took especially long or something else happened etc. Javascript doesn't ever REALLY stop execution. It says it does but especially passing between angular and jquery with multiple ajax requests it wouldn't be surprising if it was executing the second ajax call before it actually completed your angular post. That's why a refresh would replicate the error because it's would clear your variables.
Some things you can do to test this:
On the backend make a timer that goes for a few seconds before it returns anything. This will probably make your code fail more consistently.
Set breakpoints and see when they are being hit and the values they contain in the javascript.
Good luck!

Asynchronous ajax request locking browser

This is a simple snippet of code to launch an aynchronous ajax request.
The processing time of the request is deliberately long (10 seconds or more).
Why browser prevent my users to click on a href link during the process of the async request ?
(tried with Firefox and Chrome)
The async request is normally called and the 'Ready' message is immediately displayed in console.
Snippet :
new Ajax.Request('index.php', {
method: 'post',
asynchronous: true,
parameters: { 'sleep': 10 },
onSuccess: function(transport) { console.log('Success'); },
onFailure: function() { console.log('Error'); }
});
console.log('Ready');
PHP is the cause of the problem here. When you do session_start() the PHP locks the session file so there’s no concurrent writing to this file and gives the running script full access to the session variables ( reading and writing ).
So you need to call session_write_close() as soon as possible.

Efficient way of passing data and calling background script in PHP

I have a page where I show 5 questions to a user and when he clicks on Next 5 link I am sending the score of current page onbeforeunload() to the script updateScore() asynchronously using jQuery AJAX and when the call is successful the next 5 questions are displayed.
window.onbeforeunload=function()
{
$.ajax({
type: "POST",
url: "updateScore.php",
data: "pageScore="+score,
cache: false,
timeout:5000,
async:false
});
}
But the problem is that on slow connections,it might hang the browser for a while until AJAX call returns successfully.When I tried async:true(default) the next page is loaded first without making call to updateScore.php.It might be due to the fact that connection is fast in localhost hence giving no time for the AJAX call to complete.This was the reason I used async:false.Will it happen (making no AJAX call) if I use async:true in practical case as well?If yes, is there a way to come around this problem?
I advice you to change your code a bit.
Make ajax request on "click" event, and redirect user inside ajax callback function.
Like this:
$('#mybutton').on('click', function()
{
$('#pleasewait').show();
$ajax({
type: "POST",
url: "updateScore.php",
data: "pageScore="+score,
success: function() { document.location="nextpage.php" }
});
}

Loading gif image is not showing in IE and chrome

I am using JQuery ajax call for sending synchronized call to server and want to display a loading image for that time.
But loading image is visible in Firefox but not in IE and chrome. When i debug, i found that in IE, when we call java script, it stop showing changes in browser as it halts DOM and when the call is completed, it then shows all the changes. Since i shows the loading image on ajax call and remove it after completing the call, the IE doe not display any image.
But when i use alert box,it shows the loading image in IE as it stop the thread until we response to it.
Is there any method to stop java script execution for a millisecond such that IE execution halts and loading image is shown.
I already tried ajaxSend, ajaxComplete, ajaxStart, ajaxStop of JQuery and timer event of java script, but does not get any solution.
Any help is precious, thanks in advance.
In the context of XHR, synchronously means just that the browser freezes until the request is complete. If what you want is to make sure only one request is running at a given time, then use your own flag to indicate that a request is in progress.
By definition, the synchronous flag of a request means that any other activity must stop. I'm surprised that it even works in Firefox, last time I tried that it didn't work, but that was a long time ago. So forget about it, there's no way to make it work in all browsers. Even if you delay the request using a setTimeout, at best you'll get a frozen browser with a non-animated gif. And users don't like when you freeze their browser, especially if the request might take more than a fraction of a second.
Don't ever depend on the browser for security or correct functionality related features. If your application might get broken if a client does two requests in parallel, then you have a bug on the server side. There's nothing that prevents a malicious user from making parallel requests using other tools than the normal UI.
You problem is probably the 'synchronized' part in your opening post.
Open the connection asynchronously. That stops the browser from locking up, and it will work as you expect. (set async = true on your XmlHttpRequest / activex object)
try to shows the loading image at the start of your ajax jquery call and hide it on success event
or
you can use set time out also
I also faced similar problem while working with ajax like i applied some styles to UI during ajax call but these are not applied to UI and same as you told that if we put some alert it will work as expected until OK is not clicked for alert
I can't guess why it happening but you can use JQuery to show or Hide that Loading.... div
Hope it will work....
I had a similar problem and then I sorted it out by using the Update Panel in ASP.NET. If you are using PHP or any other technology then you have to use ajax call. If you do synchronized call then the Loading image will not be shown.
I have had similar situation to deal with, if you have to make the synchronous call, browser will suspend the DOM manipulation. So unless absolutely necessary, keep with the async calls.
There is a workaround for manipulating the DOM and show an element before starting the ajax call. Use jQuery animate(), and make the ajax call in the callback for animation complete. Following code works for me:
//show the loading animation div
$('#loading').show();
//call dummy animate on element, call ajax on finish handler
$('#loading').animate({
opacity: 1
}, 500, function() {
//call ajax here
var dataString = getDataString() + p + '&al=1';
$.ajax(
{
type: 'GET',
async: false,
url: 'uldateList.php',
data: dataString,
dataType: 'json',
success: function(result){
//Do something with result
...
//hide loading animation
$('#loading').hide();
}
});
});
You can try to execute the ajax synchronous call in the image load callback.
Something like:
var img = new Image();
img.src = "loading.gif";
img.onload = function() {
/* ajax synch call */
}
Then append img to DOM.
Hi try to modify ajax call like this its works for me
xmlHttp.open('POST', url, true);
xmlHttp.onreadystatechange = myHandlerFunction;
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlHttp.setRequestHeader("Accept-Charset", "charset=UTF-8");
xmlHttp.send(query);
I used Sergiu Dumitriu's information as base. I set my async to true and added
beforeSend: function() {
$('#Waiting').jqmShow();
},
complete: function() {
$('#Waiting').jqmHide();
}
And it worked for me. Basically i created my own async:false attribute.
In your $.ajax call you need to add async: true. Following code works for me:
$.ajax({
url: 'ajax_url',
data: 'sending_data',
type: 'POST',
cache : false,
async: true,
beforeSend: function() {
$('#id_of_element_where_loading_needed').html('html_of_loading_gif');
},
success: function(data) {
$('#id_of_element_where_result_will_be_shown').html(data.body);
}
});

How can I be sure my Ajax call has fully completed?

I have the following code:
$.ajax({
cache: false,
url: "/Administration/" + entity + "s/Update",
data: { pk: pk, rk: rk, fld: type, val: val },
success: function () {
disableLoadingIcon();
if (idArr.substr(0, 8) == 'Position') {
location.reload();
}
}
});
When a user changes some data the code updates the database. There is code that comes before this that picks the data values and it all works good.
When the user changes the Position column the database gets changed and I wanted to trigger a refresh of the screen (it's a report screen sorted by position). The refresh works but it seems like it is out of sync. I have the location.reload() in the success area but is it possible that is getting run before the Ajax has completed?
Is it possible that this kind of refresh is taking place before the database has been properly updated? When I do another refresh of the page manually from the browser the data always appears in the correct order.
Your document is cached. You shouold use
location.reload(true)
to realod with clear cache.
AJAX is asynchronous by default. Once the call is issued, the rest of your code will continue executing. If the value of location gets changed before the ajax call returns its data, the success function will be using the current-at-the-time-it-executes value of location, which is now something different than what it was when the ajax call started.
The success code will not run until the ajax call returns, so that's not the problem. but you're depending on location not being changed between the time you start the ajax stuff and the time it completes.
there is a API in jquery ajaxComplete. whenever a ajax call will be completed this will be invoked.
$.ajaxComplete(function(){
//do something
});
reference : http://api.jquery.com/ajaxComplete/
Whatever you write inside the success handler will be executed only after the completion of the ajax request you made to the server page. so you can load the updated content in that handler.you can use a parameter to accept the response from your success function and check whether your transaction is success or not.
From your server page, after making the database update,you can return true or false.
$.ajax({
cache: false,
url: "/Administration/" + entity + "s/Update",
data: { pk: pk, rk: rk, fld: type, val: val },
success: function (data) {
// This will be excuted only after you receive a response from your server page
if(data=="true") //db updated properly
{
disableLoadingIcon();
if (idArr.substr(0, 8) == 'Position') {
location.reload();
}
}
else
{
alert("Error in updating the data");
}
}
});

Categories