Javascript XMLHttpRequest issue - javascript

I am using the following script to monitor whether I can connect to a web site in a regular interval (10 seconds in my sample code). I met with two issues, any ideas how to solve them?
If a web site is very slow and no response within 10 seconds (making PingWebSite not return), I find 2 second call to PingWebSite will be executed because of 10 second interval arrives. My purpose is I want only one call to PingWebSite is under execution, and if 10 seconds interval arrives and previous PingWebSite is executing, I want to prevent current PingWebSite from execution. Any ideas how to solve this?
I find a strange issue, when I connect to a very slow web site, and code path executes to "alert("connecting");", then I expect exception to be thrown for timeout, but in my debug, no exception is thrown. Any ideas how to catch timeout exception?
Here is my code,
var index = 0;
function setup() {
window.setInterval(PingWebSite, (10 * 1000));
}
function PingWebSite() {
var http_request = new XMLHttpRequest();
try {
http_request.open("GET", "http://www.google.com", true);
http_request.onreadystatechange = function() {
if (http_request.readyState == 4) {
if (http_request.status == 200) {
MonitorInformation.innerText = "http://www.google.com" + " Connection ok";
alert("ok");
}
else {
alert("fail");
}
http_request = null;
} // if if (http_request.readyState == 4)
else {
// if execute here, no exception will be thrown
alert("connecting");
}
} // end of function
http_request.send(null);
} // try
catch (e) {
alert("service is not available");
}
}
EDIT 1: I have followed advice here to modify my code. Here is the below version. The new issue is index value (e.g. 0) will be prompted in alert message box before ok/fail alert message box. I think index value (e.g. 0) should be prompted in alert message box after ok/fail alert message box. Any ideas why?
var index = 0;
var http_request;
var xhrTimeout;
var chkConn;
function setup() {
chkConn = window.setInterval(PingWebSite, (10 * 1000));
}
function WebMonitorTimeout() {
http_request.abort();
alert("timeout");
index = index + 1;
}
function PingWebSite() {
http_request = new XMLHttpRequest();
http_request.open("GET", "http://www.google.com", true);
http_request.onreadystatechange = function()
{
if (http_request.readyState == 4) {
if (chkConn) { clearInterval(chkConn); }
if (http_request.status == 200) {
alert("ok");
index = index + 1;
if (xhrTimeout) { clearTimeout(xhrTimeout); }
}
else {
alert("fail");
index = index + 1;
if (xhrTimeout) { clearTimeout(xhrTimeout); }
}
http_request = null;
} //if (http_request.readyState == 4)
} // end of event function
http_request.send(null);
xhrTimeout = setTimeout("WebMonitorTimeout();", 30000);
alert(index);
chkConn = window.setInterval(PingWebSite, (30 * 1000));
}
thanks in advance,
George

Duplicate of javascript connect to web site code not working
You can't do Cross Site XHR requests because of browser security

For your first problem, don't use setInterval – use setTimeout in the callback for your request:
http_request.onreadystatechange = function () {
if (http_request.readyState == 4) {
// ...
setTimeout(PingWebSite, 10000);
}
};
Don't forget to call your function once after it has been defined to start it off (after that setTimeout will be called every time after a request has finished.)
Note that in some cases you might not reach readyState 4. I haven't really looked into how other libraries handle those cases, but look at the source code of jQuery, for example, for inspiration.

<SCRIPT language=javascript>
// Needed for IE6 and older to replicate the standard XMLHttpRequest object
if (window.ActiveXObject && !window.XMLHttpRequest){window.XMLHttpRequest =
function(){progIds=new Array("Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP",
"Microsoft.XMLHTTP");for(i in progIds){try{return new
ActiveXObject(progIds[i]);}catch(ex){alert(progIds[i]);}}return null;};}
// Standard asynchonous AJAX code
var xhr = new XMLHttpRequest();
// You would normally trade out the location.href with an actual .ashx
// page. It's like this here only for testing, thereby requesting this
// same page back from the server.
xhr.open("POST",location.href,true);
// The function that will be called asynchronously when the server sends
// back its response
xhr.onreadystatechange=function(){
// If you're using the file system instead of a web server then xhr.status
// will come back as 0, not 200. And of course if the page isn't found
// then a web server will send back a status of 404. xhr.readyState is 4
// when the page is done.
if (xhr.readyState == 4 && xhr.status == 200) {
clearTimeout(xhrTimeout); // Looks like we didn't time out!
// Use xhr.responseText to parse the server's response
alert(xhr.responseText);
}
}
// Now that we're ready to handle the response, we can make the request
xhr.send("My excellent post info");
// Timeout to abort in 5 seconds
var xhrTimeout=setTimeout("ajaxTimeout();",5000);
function ajaxTimeout(){
xhr.abort();
alert("Well dang, the AJAX request timed out. Did you lose network "+
"connectivity for some reason?");
// Note that at this point you could try to send a notification to the
// server that things failed, using the same xhr object.
}
</SCRIPT>

Related

Why do blank alert statements affect execution of other code?

I'm working on the front page of a website that will have a section that will show news articles. The articles will fade into the next one every 10 seconds. For some reason the code only executes correctly (keep in mind it's not entirely finished so there may be other errors) with the inclusion of a couple alert() statements. These were previously in there just for debugging, but currently, it seems as though they serve some functional purpose. Without them, the code will give different results if any at all.
I'm mainly a Java programmer, so there are probably some idiosyncrasies about JavaScript alert() statements that I'm not familiar with. The other odd thing that I noticed was that at times I would run the code multiple times with no changes and get different results. I used some of the alert() statements in the loadArticles() function to output the value of i and would occasionally get a different result without changing the code. The only idea I have so far is that my computer is taking time to run the statements that is allowing some other process to finish, but there shouldn't be any multi-threading involved.
The init() function is called in onload from the HTML and there's a div with id="news" somewhere in the center of the page.
On top of the main question, extra credit for anyone who could help out with why I'm sometimes not getting the articles to fade in and out. I'm pretty sure it has something to do with the article or container being null, but I haven't had time to get to that yet.
Here's the JavaScript:
var article_count = 0;
var count = 0;
function init() {
getArticleCount();
loadArticles();
changeSlide();
resize();
resize();
}
function getArticleCount() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
article_count = xmlhttp.responseText;
}
};
xmlhttp.open("GET", "getArticleCount.php", true);
xmlhttp.send();
}
function loadArticles() {
alert();
for(i = 1; i <= article_count; i++) {
alert();
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
alert();
var news = document.createElement("iframe");
news.className = "news";
news.src = "articles/" + xmlhttp.responseText;
news.style.zIndex = 0 - i;
var container = document.getElementById("news");
container.appendChild(news);
}
};
alert();
xmlhttp.open("GET", "getArticles.php?q=" + i, true);
xmlhttp.send();
alert();
}
}
function changeSlide() {
var article = document.getElementsByClassName("news")[count];
var interval = setTimeout(function() {
var fadeOut = article.fadeOut(1000, function() {
if(count < article_count) {
count++;
changeSlide();
} else {
count = 0;
resetSlides();
}
});
}, 10000);
}
function resetSlides() {
var articles = document.getElementsByClassName("news");
for(j = 0; j < article_count; j++) {
var fadeIn = articles[j].fadeIn(1000);
}
changeSlide();
}
function resize() {
var body = $(document.body);
var news = $("#news");
$("#menu_left").width((body.outerWidth() - news.outerWidth()) / 2 - 3);
$("#menu_right").width((body.outerWidth() - news.outerWidth()) / 2 - 3);
$("#menu_contact").width(body.outerWidth());
}
There are lots of mistakes in your code, mostly related to the asynchronous nature of Ajax calls. You will need to more about programming with asynchronous operations to write correctly functioning and reliable and consistent code.
alert() statement change the relative timing of asynchronous operations (such as Ajax calls vs. when your other code runs.
In general, stop using alert() statements at all as a debugging tool because it can influence the timing too much. Instead, use console.log() statements. Since console.log() just outputs to the console and does not block execution of the Javascript thread at all, it will not impact the timing of things nearly as much as an alert() statement.
Here's a trivial example to show you how an alert() can change the timing of things:
var img = new Image();
img.src = "http://somedomain.com/myimg.jpg";
alert("Press OK to continue");
if (img.complete) {
console.log("image is done loading");
} else {
console.log("image is not yet done loading");
}
With the alert statement, you will get image is done loading in the console. Without the alert, you will get image is not yet done loading. The alert has changed the flow of your code.
Another thing that can affect the timing of your code is whether resources are in the browser cache or must be loaded over the network. In nearly all cases, properly written code that only uses resources when it knows they have been loaded will continue to work in either case. But, in cases with poorly written code, you may see a different behavior the first time a page is loaded vs. subsequent times when some of the resources are now cached.
To fix your specific code, you need to program asynchronously. That means using completion handlers for asynchronous operations like Ajax calls and calling callbacks to notify other code when asynchronous operation are done.
For example, your getArticleCount() function is asynchronous. It will finish its Ajax operation some time AFTER the getArticleCount() has already returned. You can change it to accept a callback like this:
function getArticleCount(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "getArticleCount.php", true);
xmlhttp.send();
}
And, then you use it like this:
getArticleCount(function(cnt) {
// in here you can use the article count
});
As for your .fadeOut() and .fadeIn() operations, those are not native DOM methods so you can't call them on DOM objects like you are trying to do. It appears that you are attempting to use the jQuery methods with this name. To do, you must load jQuery into your page and then you must create jQuery objects that contain the relevant DOM objects and call .fadeOut() and .fadeIn() on the jQuery objects, not on the DOM objects.
Your loadArticles() function can be fixed by putting the ajax calls inside an internal function inside the method. This will allow each ajax operation you are starting to have it's own separate variables rather than having all of them collide and try to use the same variables. You can do that by using an IIFE (Immediately Invoked Function Expression) inside your for loop like this:
function loadArticles() {
for (i = 1; i <= article_count; i++) {
(function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var news = document.createElement("iframe");
news.className = "news";
news.src = "articles/" + xmlhttp.responseText;
news.style.zIndex = 0 - i;
var container = document.getElementById("news");
container.appendChild(news);
}
};
xmlhttp.open("GET", "getArticles.php?q=" + i, true);
xmlhttp.send();
})();
}
}
Note, because Ajax operations have an indeterminate timing, this code does not guarantee that the items you are adding to the page will be added in any particular order. They are more than likely to be added in the order of your for loop, but that is not guaranteed. If the serve happens to be quicker with one of the requests vs. another, it might finish first and get added to the page first, even though it was not the first one requested.
And, since your resize() function appears to use jQuery, you will find it a TON easier to use jQuery's ajax support rather than coding your own Ajax calls. Plus with jQuery Ajax, you can use the built-in promise interface to make your asynchronous programming and error handling substantially easier.
The reason removing the alert calls in your code made it not work anymore is because your functions getArticleCount(), loadArticles() are making asynchronous requests for data. Alert popups made the program halt, while the AJAX request was off retrieving data and it had returned the result by the time you closed the alert popup.
You can change those 2 functions to execute a callback function as a way to let other functions know that it's finished:
function init() {
getArticleCount(function() {
// finished getting article count
loadArticles(function() {
// finished loading articles
changeSlide();
resize();
resize();
});
});
}
function getArticleCount(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
article_count = xmlhttp.responseText;
callback(); // done
}
};
xmlhttp.open("GET", "getArticleCount.php", true);
xmlhttp.send();
}
function loadArticles(callback) {
for(i = 1; i <= article_count; i++) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var news = document.createElement("iframe");
news.className = "news";
news.src = "articles/" + xmlhttp.responseText;
news.style.zIndex = 0 - i;
var container = document.getElementById("news");
container.appendChild(news);
callback(); // done
}
};
xmlhttp.open("GET", "getArticles.php?q=" + i, true);
xmlhttp.send();
}
}
As a side note, you can debug using the browser Developer Tools, and use console.log() and debugger;
XMLHttpRequest is an asynchronous call.
You make a getArticleCount request in order to get a count of articles.
Then, you have a loop:
for (i = 1; i <= article_count; i++) {
getArticleCount request didn't complete by the time of this loop, and article_count is still equal to zero. You need to use onreadystatechange and move your consequent dependent calls into callbacks:
function init() {
getArticleCount();
}
function getArticleCount() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
article_count = xmlhttp.responseText;
loadArticles();
}
};
xmlhttp.open("GET", "getArticleCount.php", true);
xmlhttp.send();
}
function loadArticles() {
var container = document.getElementById("news");
for(i = 1; i <= article_count; i++) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var news = document.createElement("iframe");
news.className = "news";
news.src = "articles/" + xmlhttp.responseText;
news.style.zIndex = 0 - i;
container.appendChild(news);
}
};
xmlhttp.open("GET", "getArticles.php?q=" + i, true);
xmlhttp.send();
}
}
Anyway, you have some architectural problems:
Making count request apart from data requests is redundant. Moreover, the value could change while you execute your articles.
Making many HTTP requests will cause a huge performance drop.
You need to make a single PHP-file, which will return a JSON array with all articles. Then, you will able to work with its length and every item, and it will work much faster while not causing any synchronization problems.
The alerts are (somewhat) syncronizing your ajax calls. You have dependencies between getArticleCount and loadArticles. The alert() at the top of loadArticles is causing execution to pause until you cancel the alert. In that time the AJAX request to "getArticleCount.php" has completed and assigned a value to article_count.
Without the alerts pausing execution, your code is non-deterministic b/c AJAX calls can not be strung together in a procedural fashion and behave synchronously. You will need to use a functional programming style to get AJAX calls to behave synchronously.
For example you could make write it like this
function init() {
getArticleCount(loadArticles);
resize();
resize();
}
function getArticleCount(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//callback will be loadArticles()
callback.call(null, xmlhttp.responseText);
}
xmlhttp.open("GET", "getArticleCount.php", true);
xmlhttp.send();
}
function loadArticles(articleCnt) {
for(i = 1; i <= articleCnt; i++) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var news = document.createElement("iframe");
news.className = "news";
news.src = "articles/" + xmlhttp.responseText;
news.style.zIndex = 0 - i;
var container = document.getElementById("news");
container.appendChild(news);
if(i == i){
//modify function to use passed in count instead of global
changeSlides(articleCnt);
}
}
};
xmlhttp.open("GET", "getArticles.php?q=" + i, true);
xmlhttp.send();
}
}
When calling getArticleCount the callback parameter would be loadArticles which has been modified to accept the article_count as a parameter instead of using the global. The above code will fix your issue. You should modify other functions to take a local article count and stop relying on the global.

Javascript - updating textarea freezes browser tab

I am updating text in text area with javascript every 2 seconds, however sometimes happen that entire page freezes and you have to close the tab (other tabs in browser are working normally, this happens to all people visiting the page).
This is how my code looks like:
function ajaxSyncRequest(reqURL) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", reqURL, false);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send('server=" + server + "');
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200){
document.getElementById(\"1\").innerHTML = xmlhttp.responseText;
if (document.getElementById('check').checked) { document.getElementById(\"1\").scrollTop = document.getElementById(\"1\").scrollHeight; }
} else {
document.getElementById(\"1\").innerHTML = "Could not connect to remote server!";\n
}
}
}
And this is the 2 seconds timer:
function timer() {
ajaxSyncRequest("ConsoleGenerator");
window.setTimeout("timer()", 2000);
}
I am getting the text with POST method to Java Servlet. It works sometimes for hours and then it freezes and browser says "Page is not reposnding..." or sometimes it works just a few minutes and then it freezes...
Can anybody help please ?
(Assuming we fix the basic syntax errors in the code.) You're happily firing off a subsequent requests without waiting for previous ones to complete. If the ajax call ever takes more than two seconds, you'll have overlapping calls. That isn't a problem in and of itself unless your backend is serializing calls or similar, but it does set up a chaotic situation.
You're also making synchronous requests by specifying false as the third argument to the POST call. There's no need to make the request synchronous, and doing so (particularly every two seconds?!) will indeed tend to lock up the UI of the browser.
I would recommend waiting for the previous request to complete before scheduling the next one, and making the requests asynchronous so the browser UI isn't locked:
// Accept callback --------------v
function ajaxSyncRequest(reqURL, callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", reqURL, true);
// async, not sync ----------^
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send('server=" + server + "');
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById("1").innerHTML = xmlhttp.responseText;
if (document.getElementById('check').checked) {
document.getElementById("1").scrollTop = document.getElementById("1").scrollHeight;
}
} else {
document.getElementById("1").innerHTML = "Could not connect to remote server!\n";
}
callback(); // <== Call it
}
}
function timer() {
ajaxSyncRequest("ConsoleGenerator", function() { // Pass in a callback
setTimeout(timer, 2000);
});
}
That will wait for the ajax to complete and then schedule another update two seconds later. If you want to include the time the ajax call took in the two seconds instead, we can do some basic math:
function timer() {
var started = Date.now();
ajaxSyncRequest("ConsoleGenerator", function() { // Pass in a callback
setTimeout(timer, Max.max(0, 2000 - (Date.now() - started));
});
}
Side note: No need for the window. prefix on setTimeout (though it's harmless provided nothing's shadowed the global window), and rather than passing a string to it, just pass a function reference.

limit time to execute an if statement in javascript

I've got some code that checks if a user is connected to the internet. It works perfectly, but in the application I have things need to move quickly. In the case that they're connected to a really slow or non-functioning network my statement takes a long time to evaluate to false and really slows things down. If they're connected it retrieves the 1px image in no time at all.
My question is. How can I modify this function to return "false" if it takes more than a second to run the XML request? Can't seem to find an easy way to do this, but I am new to javascript so sorry if I'm missing something obvious.
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "/assets/LuPixel.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true; //alert("TRUE");
} else {
return false; //alert("FALSE");
}
} catch (e) {
return false; //alert("FALSE");
}
}
UPDATE: thought I needed a little more context.
What I'm trying to do with the above function is test whether a connection exists, and then access a certain URL if it does, if it doesn't, store some info in a local array. As soon as I add timeout however, it breaks the existing functionality.
ANother part that may be complicating things is that I'm actually loading up all survey questions inintially, then using javascript to cycle out which ones display. At the end of the survey it either submits results and refreshes the page (if connected to the internet) or just shows the first question again and continues storing results in the "responses" object (if not connected to the internet) additionally each time a question is clicked it also attempts to use the above doesConnectionExists() function to determine whether to submit results as the javascript transitions to the next question. the questions variable below contains the number of questions left to show
Here is all the javascript from the page.
<script>
//prevents scrolling
document.body.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
var questions = {{count($questions)}};
var isIOS = ((/iphone|ipad/gi).test(navigator.appVersion));
var myDown = isIOS ? "touchstart" : "mousedown";
var myUp = isIOS ? "touchend" : "mouseup"
var responses = '';
// Hide every div except the first one
$('.survey-container:not(:first)').hide();
$('button').bind(myUp, finish);
//$('button').bind('touchcancel', cancel);
function finish(){
var answerId = this.id;
var survey_id = {{$survey->id}};
var location_id = {{$location_id}};
//CYCLE THROUGH QUESTIONS (via survey-container)
// Hide the current div when we click the link
$(this).parent().parent().next().show();
$(this).parent().parent().hide();
responses += answerId+'.'+Math.floor(Date.now() / 1000)+' ';
//add responses
if(doesConnectionExist() == true) { //old navigator.onLine == true
//send response and reset responses
new Image().src = '/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses;
responses = '';
}
//submit and reset responses here
//reset responses if submitted with: responses = '';
questions = questions-1;
if(questions == 0) {
$('.survey-container:first').show();
questions = {{count($questions)}};
if(doesConnectionExist() == true) {
$('.darken, .thankyou').show();
//submit results after a short timeout for thankyou message
setTimeout(function() {
new Image().src = '/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses;
location.reload();
//window.location.assign('/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses);
}, 800);
} else {
$('.darken, .thankyou').show();
setTimeout(function() {
$('.darken, .thankyou').fadeOut(400);
}, 1200);
}
} else {
$('.darken, .thankyou').show();
setTimeout(function() {
$('.darken, .thankyou').fadeOut(400);
}, 1200);
}
}
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "/assets/lupoll_light.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
</script>
You can use async requests with timeout and abort if time limit exceeded.
var xmlhttp = getXmlHttp()
xmlhttp.open("POST", "/someurl", true); // true - activate async request method
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState != 4) return
clearTimeout(timeout) // clear timeout on readyState 4
if (xmlhttp.status == 200) {
// all ok
...
alert(xmlhttp.responseText);
...
} else {
handleError(xmlhttp.statusText) // error callback
}
}
xmlhttp.send("a=5&b=4");
// 10 second timeout
var timeout = setTimeout( function(){ xmlhttp.abort(); handleError("Time over") }, 10000);
function handleError(message) {
// error callback function
...
alert("error: "+message)
...
}
Add timeout to the xhr object:
xhr.timeout = 1000;
You can specify the timout for the request:
xhr.timeout = 1000;
Besides setting the timeout, you should make the function asynchronous. That way you can allow a bit longer timeout if you with, and the application will still not be sluggish.
function doesConnectionExist(callback) {
var xhr = new XMLHttpRequest();
var file = "/assets/LuPixel.png";
var randomNum = Math.floor(Math.random() * 10000);
xhr.timeout = 1000;
xhr.onreadystatechange = function() {
if (xhr.status >= 200 && xhr.status < 304) {
callback(true);
} else {
callback(false);
}
};
xhr.open('HEAD', file + "?rand=" + randomNum, true);
xhr.send();
}

Chrome doesn't redraw page when needed

I've searched SO for similar issues (e.x. Chrome does not redraw <div> after it is hidden and Force DOM redraw/refresh on Chrome/Mac ) but none of questions gave me the solution to my problem. I am writing modem configuration panel, the webpage with ,,tabs''. On every tab there are some settings-just like configuration panel of any router.
Saving configuration (done when user clicks on Save button) takes few seconds (my embedded platform is not a speed king), so I decided to put special PLEASE WAIT window (div to be precise) which is usually hidden, but is shown when needed to calm user down :-).
Everything works fine on Firefox: after clicking save, the PLEASE WAIT div shows and then the configuration is saved using POST method. However, on Chrome 26 and Chromium 25 the div does not show until the configuration is saved. As you can see in SaveConfiguration function after executing PHP script that saves configuration the alert is shown-this is where the PLEASE WAIT div shows up on Chrome. It looks like Chrome is not redrawing page but immediately starts launching POST script. Has anyone had similar issues and now how to fix this problem?
Below are fragments of my code, I have only supplied functions that might give a clue what I'm doing. I can post more code if that helps.
function showLoadingScreen(yes)
{
if(yes)
{
document.getElementById("loadingtext").innerHTML="Please wait...";
document.getElementById("loading_overlay").style.display="block";
document.getElementById("loading_window").style.display="block";
}
else
{
document.getElementById("loading_overlay").style.display="none";
document.getElementById("loading_window").style.display="none";
}
}
function postDataSync(url, params)
{
var XMLHttpRequestObject = false;
if (window.XMLHttpRequest)
{
XMLHttpRequestObject = new XMLHttpRequest();
} else
if (window.ActiveXObject)
{
XMLHttpRequestObject = new
ActiveXObject("Microsoft.XMLHttp");
}
if(XMLHttpRequestObject)
{
XMLHttpRequestObject.open("POST", url, false);
XMLHttpRequestObject.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
XMLHttpRequestObject.send(params);
{
if (XMLHttpRequestObject.readyState == 4 &&
XMLHttpRequestObject.status == 200)
{
var result = XMLHttpRequestObject.responseText;
delete XMLHttpRequestObject;
XMLHttpRequestObject = null;
return result;
}
}
}
return '';
}
function SaveConfiguration()
{
var errors=checkForm();
if(errors!="")
{
printError("Can't save configuration because there are errors in current tab:<br><br>"+errors);
return;
}
showLoadingScreen(true);
saveTab();
var retval=postDataSync('actions/saveconf3.php','');
alert("Settings saved. The modem is now being reconfigured.");
document.location = "http://" + retval;
}
You are using ajax synchronously rather than asynchronously meaning javascript execution halts during the request. To fix make the following change:
XMLHttpRequestObject.open("POST", url, true);
You need to use a callback for the behaviour after the request is complete. Something like this:
function postDataSync(url, params, success)
{
var XMLHttpRequestObject = false;
if (window.XMLHttpRequest)
{
XMLHttpRequestObject = new XMLHttpRequest();
} else
if (window.ActiveXObject)
{
XMLHttpRequestObject = new
ActiveXObject("Microsoft.XMLHttp");
}
if(XMLHttpRequestObject)
{
XMLHttpRequestObject.open("POST", url, true);
XMLHttpRequestObject.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
XMLHttpRequestObject.send(params);
XMLHttpRequestObject.onreadystatechange = function() {
if (XMLHttpRequestObject.readyState == 4 &&
XMLHttpRequestObject.status == 200)
{
var result = XMLHttpRequestObject.responseText;
delete XMLHttpRequestObject;
XMLHttpRequestObject = null;
if (typeof success === 'function') success(result);
}
}
}
return '';
}
function SaveConfiguration()
{
var errors=checkForm();
if(errors!="")
{
printError("Can't save configuration because there are errors in current tab:<br><br>"+errors);
return;
}
showLoadingScreen(true);
saveTab();
postDataSync('actions/saveconf3.php','', saveComplete);
}
function saveComplete(result) {
showLoadingScreen(false);
alert("Settings saved. The modem is now being reconfigured.");
document.location = "http://" + result;
}
If you have heavy synchronous code (in practice, operations on hundreds or thousands of objects that are already in memory, or calculating pi to a gazillion digits) you can use setTimeout to give the browser time to catch up with any rendering tasks. You'd either need to call setTimeout for each task, or if you have a long-running task, split it up in batches first. This requires quite a bit of refactoring though, since every task needs to be represented as a function that can be passed to setTimeout.
I wouldn't use XMLHTTPRequest synchronously ever.
If setTimeout(fn, 0) does not trigger the "incremental" rendering, try a higher value, until it works. I think I needed to use a value of 100ms between jobs in some cases, for some browsers (I don't recall which).
You may need to yield to the browser even quicker if you want to achieve 60fps, or 30fps. Then you need to stay under 16ms or 33ms for each task. That gets very tight on slow hardware, such as (older types of) smartphones. Then, instead of setTimeout, you can best use requestAnimationFrame, if available.

Ajax send function wont' fire?

Whenever I run this file the code runs up to the point where the send function fires and then it only fires if I have an alert function directly behind it, if I take out the alert("sent"); out then it replies with ServerReadyyState is:1.
What could possibly be the problem? Someone please help, I've tried it on my local machine and on my personal server and got the same results. Any help is greatly appreciated.
The Code:
/**
* #author d
*/
var xhr;
function getPlants(xhr) {
try {
xhr = new XMLHttpRequest();
} catch (microsoft) {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
xhr = false;
alert("ajax not supported");
}
}
}
xhr.open("GET", "db_interactions.php", true);
xhr.send(null);
alert("sent"); //the send function only works if this alert functions is here
if (xhr.readyState == 4) {
return xhr.responseText;
} else {
alert("Server ReadyState is:" + xhr.readyState);
xhr.abort();
//getPlants(xhr);
}
}
AJAX is asynchronus. You can't just check the ready state immediately after.
The correct design pattern is to assign a function for the AJAX call to run when the ready state changes.
xhr.onreadystatechange = function () { alert('It changed!') }
In that function, you'll want to check if the state is 4. If so, you're ready to process the output. If not, do nothing, since that function will be called a few times before the ready state is 4.
Requests take some amount of time. When adding an alert() the code is being stopped until the user clicks ok. So when you remove it the request is send and immedialty checked. Resulting in an unfinished request.
When you change your code to this:
xhr.onreadystatechange=state_change
xhr.send(null);
function state_change() {
if(xhr.readyState==4) {
return xhr.responseText;
} else {
alert("Server ReadyState is:"+xhr.readyState);
}
}
a certain function like in this case state_change gets called every time the state changes. So you can wait until the request is finished or until an errorcode comes up.

Categories