XHR State Error when readyState is as expected - javascript

I am writing a JavaScript program which requires significant usage of XHR methods. I am using the asynchronous style. This is the core of what I have.
global = {};
global.xhr = {};
global.xhr.requestQueue = [];
global.xhr.responseQueue = [];
xhr = new XMLHttpRequest();
xhr.first = true;
xhr.onreadystatechange = function() {
//console.log(this.readyState);
if(this.readyState == 4) {
if(this.first) {
this.first = false;
global.carouselItems = parseInt(this.responseText);
global.onSlideCountReceived();
} else {
global.xhr.responseQueue.push({status: xhr.status, responseText: xhr.responseText});
if(global.xhr.requestQueue.length > 0) {
xhr.open("GET", global.xhr.requestQueue[0]);
global.xhr.requestQueue.shift();
}
}
}
if(xhr.readyState == 1) {
xhr.send();
}
}
//Code which adds a bunch of items to global.xhr.requestQueue
xhr.open("GET","assets/carousel/items.php");
But when I run this, the Chrome Dev console prints a bunch of errors like the following.
Uncaught InvalidStateError: Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.
This is despite the only call to xhr.send() being in the snippet above, wrapped in its if statement. This is confusing to me, because the way I see it suggests that I make a call to xhr.send() when xhr.readyState is 1, equivalent to XMLHttpRequest.OPENED and yet I get a complaint about this not being the case.
By the way, the requests all complete successfully, and global.xhr.responseQueue is populated with expected response info. These are all intra-domain requests.

Related

Uncaught TypeError: Cannot read property 'items' of null XMLHttpRequest JAVASCRIPT

I am trying to access YouTube v3 API and getting user info using access token. I am getting full JSON in response with all details with i want but this error also come with that.
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET',
'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true&' +
'access_token=' + access_token);
xhr.onreadystatechange = function (e) {
var channel = xhr.response.items[0]; **// This is the error line**
document.getElementById('token').value = user.Zi.access_token;
};
xhr.send(null);
console log
Uncaught TypeError: Cannot read property 'items' of null
at XMLHttpRequest.LoadData.xhr.onreadystatechange
LoadData.xhr.onreadystatechange # main.js:93
XMLHttpRequest.send (async)
LoadData # main.js:101
setSigninStatus # main.js:65
updateSigninStatus # main.js:114
(anonymous) # cb=gapi.loaded_0:267
c.port1.onmessage # cb=gapi.loaded_0:153
You're not waiting for the response to be complete, and you're not checking for success. You need to check readyState === 4 to know whether the response is complete, and status === 200 to know if the request was successful:
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET',
'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true&' +
'access_token=' + access_token);
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
// Request is complete -- did it work?
if (xhr.status === 200) {
// Success
var channel = xhr.response.items[0];
document.getElementById('token').value = user.Zi.access_token;
} else {
// Failure, handle/display error
}
}
};
xhr.send(null);
Side note: You don't appear to be using channel after retrieving it. Instead, you're using some user variable that the code closes over. I assume your real code will use channel. :-)
Side note 2: In modern environments, you might use fetch instead of XMLHttpRequest. But if you do, beware of the footgun in the API that I describe on my blog (TL;DR - be sure to check ok before calling any of the body load methods).

Track javascript runtime errors

Is there any way to capture all type of console errors?
Actually, I want to store all console errors to the database so I can fix serious issues of my PHP website.
Here is my current code to catch errors but this code is not capturing internal server errors and some other kind of errors like
www-widgetapi.js:99 Failed to execute 'postMessage' on 'DOMWindow':
The target origin provided ('https://www.youtube.com') does not match
the recipient window's origin
Current script that I have
function ErrorSetting(msg, file_loc, line_no) {
var e_msg=msg;
var e_file=file_loc;
var e_line=line_no;
var error_d = "Error in file: " + file_loc +
"\nline number:" + line_no +
"\nMessage:" + msg;
if(logJsErrors){
theData = "file="+file_loc+"&line="+line_no+"&err="+msg;
ajaxCtrl(
function(){
return true;
},Helpers.RootURL()+"/logs/index",theData
);
}
if(isDebugging){
console.error(error_d);
}
return true;
}
window.onerror = ErrorSetting;
I really appreciate your efforts.
Thank you :)
You could perform a kind of prototype of console.log, when you call console.log an ajax is fired, so you could do somethig like this:
(function () {
var postCons = console.log;
console.log = function (err) {
var xhttp = new XMLHttpRequest();
postCons.apply(this, arguments);
xhttp.open("POST", "log.php", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {//you could avoid this step
alert(this.responseText);//response from php
}
};
xhttp.send("data="+encodeURI(err));
}
})()
In log.php you could do whatever you want with $_POST['data'] , you could use something like urldecode($_POST['data']) .

Maximum call stack size exceeded error when using ajax multiple times

I am writing a piece of code that should catch all ajax responses on a page. I managed to do this by overriding XMLHttpRequest.onreadystatechange for javascript direct ajax or by using ajaxComplete() for jQuery ajax.
My problem happens when I try to use ajax multiple times without creating a new XMLHttpObject, for example:
var xhr = new XMLHttpRequest();
xhr.open("GET", '/echo/json/', true);
xhr.send();
xhr.open("GET", '/echo/json/', true);
xhr.send()
This makes my code go haywire and I get the maximum stack error.
Here is my testing code on JSFiddle: http://jsfiddle.net/zxCfW/
var s_ajaxListener = {};
s_ajaxListener.tmpSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
console.log('additional state change');
};
XMLHttpRequest.prototype.send = function() {
s_ajaxListener.tmpOnReadyStateChange = this.onreadystatechange;
this.onreadystatechange = function() {
if (s_ajaxListener.tmpOnReadyStateChange){
s_ajaxListener.tmpOnReadyStateChange.apply(this, arguments);
}
if(this.readyState == 4 && this.status == 200) {
s_ajaxListener.callback();
this.onreadystatechange = s_ajaxListener.tmpOnReadyStateChange;
}
};
s_ajaxListener.tmpSend.apply(this, arguments);
};
$(document).ajaxComplete(s_ajaxListener.callback);
I believe this happens because the ajax calls are asynchronous so the original onreadystatechange doesn't reset to its default value, but I don't know how to solve this.

using XMLHttpRequest to get a web page but only get one part of the html

I use the code below to get a web page(html)
var htmlString=null;
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.yahoo.com");//can change to any web address
xhr.onreadystatechange = function() {
htmlString=htmlString+xhr.responseText;
if(xhr.statusText=="200 OK\r" ){
log (global.htmlString.length);
}
}
but it always get one part of the page, rather than whole html code
Is there any parameter to set the length of the return html code?
Your comment welcome
There will be multiple readystatechange events. The request will only be completely done when xhr.readyState === XMLHttpRequest.DONE === 4.
Also, htmlString = null; ...; htmlString=htmlString+xhr.responseText; is bogus in many ways. First time around it will do htmlString = null + "text" === "nulltext. Afterwards it will add .responseText (as retrieved so far) again and again, while going through the states.
Also, on a related note, you should check xhr.status == 200, not xhr.statusText == randomString. Web servers aren't necessarily sending "OK" in case of 200.
Read the XMLHttpRequest documentation.
Something like this should work better:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.yahoo.com");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 /* DONE */) {
console.log(xhr.responseText.length);
// Do something with it...
}
}
xhr.send();

Multiple Web Worker ajax requests and not all returning

I have the following code in a web worker:
self.addEventListener('message', function(e){
try {
var xhr=new XMLHttpRequest()
for(var i = 0; i < e.data.urls.length;i++){
xhr.open('GET', e.data.urls[i], true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304 || xhr.status ==0) {
postMessage(xhr.responseText);
} else {
postMessage(xhr.status + xhr.responseText);
throw xhr.status + xhr.responseText;
}
}
};
}
} catch (e) {
postMessage("ERROR:"+e.message);
}
}, false);
e.data.urls contains 16 requests which are handled on the UI thread like this:
var replies = 0;
worker.addEventListener('message', function(e){
replies += 1;
});
Only 10 requests complete, is this because the UI thread has stopped before all the requests have returned or is there something else I am missing?
What is happening here is that your xhr variable gets overwritten in the loop. Due to the nature of XMLHttpRequest, that is, it is asynchronous by default, after the xhr.send(); line execution doesn't wait so for enters the next loop and the xhr.[...] lines operate on the xhr object set up and fired in the previous loop. Depending on whether the previous loop's request has returned (and thus the state change handler executed) or not (which is quite unpredictible) you overwrite either a 'live' or a 'finshed' xhr object. Those that get overwritten before they are finished are lost.
You should make sure you do not overwrite. Do not operate on the same XMLHttpRequest object but instantiate a new for each request.
I moved the definition of the handler function outside the loop. There's no need to redefine it in each loop. It is called in the context of the XMLHttpRequest instance it is assigned to so this points to the instance.
Also, I swapped the xhr.send() and the xhr.onreadystatechange = ... lines. A state change event handler should be assigned before the request is sent (there are several events fired right from the starting of the send) and althought unlikely, a request may even return with a ready state 4 before the line that adds the event handler is executed in the code.
self.addEventListener('message', function(e){
var xhrs = [];
function handler() {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 304 || this.status ==0) {
postMessage(this.responseText);
} else {
postMessage(this.status + this.responseText);
throw this.status + this.responseText;
}
}
};
for(var i = 0; i < e.data.urls.length;i++) {
xhrs[i] = new XMLHttpRequest();
xhrs[i].open('GET', e.data.urls[i], true);
xhrs[i].setRequestHeader('Accept', 'application/json');
xhrs[i].onreadystatechange = handler;
xhrs[i].send(null);
}
}, false);
Your example is similar to this firefox example except for the loop inside the worker making multiple ajax requests. I'd be curious to know what is causing the failure. You might be hitting a limit on the number of concurrent ajax connections a single worker can process.
Could you try moving the url for loop to the main gui thread:
for(var i = 0; i < urls.length; i++){
worker.postMessage(urls[i]);
}
and change your worker to just do a single ajax call at a time?
self.addEventListener('message', function(e){
try {
var xhr=new XMLHttpRequest()
xhr.open('GET', e.data, true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304 || xhr.status ==0) {
postMessage(xhr.responseText);
} else {
postMessage(xhr.status + xhr.responseText);
throw xhr.status + xhr.responseText;
}
}
};
} catch (e) {
postMessage("ERROR:"+e.message);
}
}, false);
and see if that works?
The mozilla example has some error handlers that might reveal the problem. You might try adding in the main GUI thread:
worker.onerror = function(error) {
dump("Worker error: " + error.message + "\n");
throw error;
};
and inside the worker:
function errorReceiver(event) {
throw event.data;
}

Categories