I have a Google Chrome extension that upon pressing a button executes an xmlhttp request to a server. I'm currently handling the error if the server is down. What I want to achieve is to retry the request after an increasing number of seconds. That's how I do it:
var execute = function(method, url, i){
//if timeout doesn't exist, create one
if(!i){
i = 1000;
}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open(method, url, true);
xmlhttp.onreadystatechange = function(){
if (xmlhttp.status != 200) {
// Handle error, retry request
console.log("xmlhttp.status = " + xmlhttp.status + " and xmlhttp.readyState = " + xmlhttp.readyState);
setTimeout("execute('"+method+"', '"+url+"', "+i*2+")", i);
return;
}
};
xmlhttp.send(null);
}
Basically if there is some kind of problem I retry again the request. If the server is up, there is no problem at all, but if the server is down JavaScript throws me an error saying:
PUT http://localhost:3000/buy/2/id=1329664820124
executepopup.html:127
(anonymous function)
Which is not really meaningful, but however. The status in the log says "0", which is pretty lame too. If I turn on the server again while this is going on, it should stop doing this, but instead also if it succeeds (I see a log in the server that tells me that it received the request), it keeps calling the execute method. I don't know how to stop this recursion if the server turns on. Am I doing something wrong? Is this state equal zero the problem?
Thanks a lot
Solving the problem
onreadystatechange is triggered for every state change, from state 0 to 4, and many times between. You should only be interested in readyState 4, because the request has fully finished at that point.
To get your method to work, check whether xmlhttp.readyState == 4:
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status != 200) {
Fixing your horrible implementation of setTimeout
setTimeout("execute('"+method+"', '"+url+"', "+i*2+")", i); is not the right way to call a function again. Since you're developing in a Chrome extension, you can use the following format for setTimeout:
setTimeout(execute, i, method, url, i*2);
// Calls execute(method, url, i*2) after i milliseconds.
Related
I'm running code like this - for a while:
xmlhttp.onreadystatechange = function () {
// Wait to get query result
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) // Request finished
{
// Do something with the responsexmlhttp.response;
}
}
var request = "something.php?operation=bla-bla";
xmlhttp.open("GET", request, true);
xmlhttp.send();
It was working perfectly until some weeks ago. Error code 500 or 404 is catched by the browser (or by the code).
However - running the request directly in the address bar never return error.
I tried to reinstall google-chrome. Didn't help.
I run the code in firefox - got the some problem.
Any idea where to dig?
Thanks, Yaakov.
I have been using jquery libraries for implementing AJAX. it was ok and I am comfortable with that. However, I started reading some ajax book and found the following code.
// stores the reference to the XMLHttpRequest object
var xmlHttp = createXmlHttpRequestObject();
// retrieves the XMLHttpRequest object
function createXmlHttpRequestObject()
{
// will store the reference to the XMLHttpRequest object
var xmlHttp;
// if running Internet Explorer
if(window.ActiveXObject)
{
try
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
xmlHttp = false;
}
}// if running Mozilla or other browsers
else
{
try
{
xmlHttp = new XMLHttpRequest();
}
catch (e)
{
xmlHttp = false;
}
}
// return the created object or display an error message
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}
// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
// proceed only if the xmlHttp object isn't busy
if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
{
// retrieve the name typed by the user on the form
name = encodeURIComponent(document.getElementById("myName").value);
// execute the quickstart.php page from the server
xmlHttp.open("GET", "quickstart.php?name=" + name, true);
// define the method to handle server responses
xmlHttp.onreadystatechange = handleServerResponse;
// make the server request
xmlHttp.send(null);
}
else
// if the connection is busy, try again after one second
setTimeout('process()', 1000);
}
//executed automatically when a message is received from the server
function handleServerResponse()
{
// move forward only if the transaction has completed
if (xmlHttp.readyState == 4)
{
// status of 200 indicates the transaction completed successfully
if (xmlHttp.status == 200)
{
// extract the XML retrieved from the server
xmlResponse = xmlHttp.responseXML;
// obtain the document element (the root element) of the XML structure
xmlDocumentElement = xmlResponse.documentElement;
// get the text message, which is in the first child of
// the the document element
helloMessage = xmlDocumentElement.firstChild.data;
// update the client display using the data received from the server
document.getElementById("divMessage").innerHTML =
'<i>' + helloMessage + '</i>';
// restart sequence
setTimeout('process()', 1000);
}
// a HTTP status different than 200 signals an error
else
{
alert("There was a problem accessing the server: " + xmlHttp.statusText);
}
}
}
Here my question is why do we use setTimeout('process()', 1000); in handleServerResponse() function? Can't we do this without setTimeout('process()', 1000);?
For me, it looks like some kind of constant polling. It's reusing the AJAX request over and over every second, and when the previous request is still active, it waits another second to send it again. So it's not just create an AJAX request and deal with the response.
Using that code, the page would be updating constantly with the information retrieved from the server. Whenever server response has changed, page will as well but not in real time (only when next request finishes). It's similar to Periodic Refresh.
As an evolution, you can have Long Polling in which you spawn an AJAX request and then wait until server responds. If any info is there in the server for you, you'll receive the response immediately. If, while you are waiting for response, anything comes to the server for you, you will receive it. If your request times out, server will respond with an empty body. Then, your client will spawn another AJAX request. You can get some more info from the Wikipedia. Extra link: Comet.
In the given example , the book has call the process() function on the body onload event.
When I change the code to onload-> to onkeyup <input type="text" id="myName" onkeyup="process()"/> I could remove the code //setTimeout('process()', 1000);
I'm trying to make a very simple script that should keep me logged on a site, that deletes your session after 10 minutes of inactivity. This is quite simple, as follows:
//Silently "refresh" the page - at least server thinks you refreshed, thus you're active
function call() {
var req = new XMLHttpRequest();
//Load current url
req.open("GET",location.href,true);
//Try to only send the data! (does not work - the browser also receives data)
req.onprogress = function() {
this.abort(); //Abort request
wait(); //Wait another 5 minutes
}
//Repeat request instantly if it fails
req.onerror = call;
//Send
req.send();
}
function wait() {
//5minutes timeout
setTimeout(call,5000);
}
wait();
This works perfectly but the requests seem to load completely. Though the page is small, I want to make this clean and prevent downloading the data. This means, I want to stop loading just after the data starts downloading. Or better - after the data has been sent.
Is there a way to make such "ping" function?
I tried this code:
var req = new XMLHttpRequest();
req.onreadystatechange = function(){
console.log( this.readyState );
if( this.readyState == 2 ) { //sent, otherwise it raises an error
console.log( 'aborting...' );
this.abort()
}
if( this.readyState == 4 ) {
console.log( this.responseText );
}
}
req.open( 'get', .... );
req.send();
prints:
1
2
aborting...
3
4
undefined
I am not completely sure with this, but I guess that by aborting the request aborts the download and retrieval of data, but all other states are triggered. I try this with a large image which was not cached and the request was done very quickly, without any result.
BTW. To just send a »ping« to your server you can also set the src of an image tag to the desired script, this will trigger the request too.
I'm playing around with this XmlHttpRequest thing. In some tutorials and books, it is the onload function the one that is called when the request is done. In my little experiment, this function is never called. Here's my code:
window.onload = function() {
var url = "http://www.google.com";
var request = new XMLHttpRequest();
request.onload = function() {
var state = this.readyState;
var responseCode = request.status;
console.log("request.onload called. readyState: " + state + "; status: " + responseCode);
if (state == this.DONE && responseCode == 200) {
var responseData = this.responseText;
alert("Success: " + responseData.length + " chars received.");
}
};
request.error = function(e) {
console.log("request.error called. Error: " + e);
};
request.onreadystatechange = function(){
console.log("request.onreadystatechange called. readyState: " + this.readyState);
};
request.open("GET", url);
request.send(null);
};
I'm testing this on the last Firefox release (just updated today). The log line in onload is never printed, and the breakpoint I set in the first line is never hit. However, the onreadystatechange function is called twice, and the http request is actually made. This is what firebug's console shows:
request.onreadystatechange called. readyState: 1
GET http://www.google.com/ 302 Found 174ms
request.onreadystatechange called. readyState: 4
NS_ERROR_FAILURE: Failure
request.send(null);
There's an error in the send line. I've tried changing it to request.send() with identical result.
At first I thought this might be the browser trying to prevent XSS, so I moved my html driver page to a Tomcat instance in my dev machine, but the result is the same.
Is this function guaranteed to be called? As I've said above, it's common to be seen in most tutorials, but on the other hand in the W3C spec page, the hello world snippet uses onreadystatechange instead:
function processData(data) {
// taking care of data
}
function handler() {
if(this.readyState == this.DONE) {
if(this.status == 200 &&
this.responseXML != null &&
this.responseXML.getElementById('test').textContent) {
// success!
processData(this.responseXML.getElementById('test').textContent);
return;
}
// something went wrong
processData(null);
}
}
var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "unicorn.xml");
client.send();
It checks readyState == this.DONE. DONE has the value 4, which is what I can see in my log. So if this were a XSS related issue, and the browser were preventing me to make the connection to a different domain, then why the actual connection is made and the DONE status is received???
PS: Yes, I know there are powerful libraries to do this easily, but I'm still a JavaScript noob so I'd like to understand the low level first.
UPDATE:
I've changed the URL to one inside my domain (localhost), and the error is gone but the onload function is still not being called. Tested in IE8 and does not work. Tested in Chrome and works. How's that?
UPDATE 2:
Tested again in Firefox, and now it works. Probably the old page was still cached so that's why I couldn't notice it immediatly. Still failing in IE8, I'll try to test it in a newer version.
It looks like it was indeed a XSS issue and Firefox was blocking the onload call. I can't still understand why the http network request was actually being done and the onreadystatechange was being called with the DONE readyState.
I changed the URL to another one in the same domain, and now it works in Firefox (after some cache-related false attempts) and in Chrome. It still does not work in IE8, despite the official docs say it is supported. I've found this SO answer which states otherwise. It looks like the onload function is a more modern convenience method and the old way of checking the result is using onreadystatechange instead.
I guess I'll accept this answer as the solution unless a more detailed answer is provided.
The onload handler won't be called for yet another reason, I'm adding it here just so it can be helpful to someone else referencing this page.
If the HTTP response is malformed, the onload handler will not be called either. For example, a plaintext response of 10 bytes that advertises a length of 14 in Content-Length header will not invoke the onload handler. I wasted hours on client code before I start to replace back-end units with test stubs.
IE has different method to create xmlhttprequest.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
};
same this article:https://www.html5rocks.com/en/tutorials/cors/
From the reference I read in MDN, it says
If TRUE (the default), the execution of the JavaScript function will continue while the response of the server has not yet arrived.
This is the A in AJAX.
I have been using AJAX but then I was a little confused when I read that. I think the problem may be that I am not understanding AJAX concept clearly. I know of course AJAX does not refresh the page which means the connection to the server and the response are completely done in the background.
But what I can imagine happening according to that reference is that if I have a code like this in my JavaScript:
//true, therefore process the function while server retrieves url
var xmlResponse;
var url = "http://example.com/file.xml";
xml_req.open("GET", url, true);
xml_req.onreadystatechange = function() {
if(xml_req.readyState == 4 && xml_req.status == 200) {
if(xml_req.responseText != null)
xmlResponse = xml_req.responseXML; //server response may not yet arrive
else {
alert("failed");
return false;
}
};
xml_req.send(null);
Doesn't that mean xmlResponse could be undefined in the sense that the server is still retrieving the data? Could somebody explain what really is the flow of the execution in AJAX technology? Thanks in advance.
I wrote a more detailed article here, but this is the basic idea.
Setting it to true means you are making an asynchronous request. That means the code does not pause until the http request is complete. A synchronous call locks up the browser so nothing else runs. That can cause problems, so people prefer asynchronous.
The XHR object updates us on what it is doing. It gives us the updates with the onreadystatechange event. We register a function with it so we can keep track of its status. The onreadystatechange gets called 4 times. Each with a different state
0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete
The data is available to us when the readystate is 4.
Now in the code you posted, it is checking for the complete state and it makes sure that the status is 200 [ok]
if(xml_req.readyState == 4 && xml_req.status == 200){
The value for xmlResponse will be undefined if you try to use it somewhere else in the code before it is returned. An example
ml_req.send(null);
alert(xmlResponse );
One of the very first articles on the XMLHttpRequest article might be a good read for you. Apple Article on xmlhttpreq
The important thing to understand is that your onreadystatechange handler is not executed immediately. And it is executed more than once. It may be easier to conceptualize, if you break the pieces out into individual functions:
function makeRequest(url)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = receiveResponse;
xhr.send();
}
function receiveResponse(e)
{
if (this.readyState == 4)
{
// xhr.readyState == 4, so we've received the complete server response
if (this.status == 200)
{
// xhr.status == 200, so the response is good
var response = this.responseXML;
...
}
}
}
First, makeRequest is called and then exits. Then, as soon as we hear anything back from the server, receiveResponse is called. Each time, we check to see if the response is fully received, and only then do we continue to process that response.