2

I can't figure out why this fiddle throws

Uncaught Error: InvalidStateError: DOM Exception 11

function url(){
 return '/echo/js/?js=' + 5 + Math.floor(Math.random()*900);
}

function poll(done){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if(xhr.status === 200 && xhr.readyState === 4){
            var foo = xhr.responseText;
            done(parseInt(foo));
        }
    };
    xhr.open('get',url(),false);
    xhr.send(null);
}

var button = document.querySelector('#poller');
var price = document.querySelector('#price');

button.addEventListener('click', function(){
    poll(function(data){
        price.innerText = data;
    });
},false);
bevacqua
  • 47,502
  • 56
  • 171
  • 285
  • 1
    Your fiddle works well for me. On which line do you get that error message? – Bergi Mar 25 '13 at 19:26
  • after clicking the button – bevacqua Mar 25 '13 at 19:26
  • @EricLeschinski Not true, `readyState` is immediately available...it's a property of `XMLHttpRequest` – Ian Mar 25 '13 at 19:27
  • And why are you sending a synchronous request (`false` third parameter) yet using the `onreadystatechange` method? – Ian Mar 25 '13 at 19:30
  • I was just testing out different setups – bevacqua Mar 25 '13 at 19:31
  • 1
    If you look at the MDN docs, it says not to use `onreadystatechange` with synchronous requests... https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#Properties – Ian Mar 25 '13 at 19:32
  • I didn't even notice the `false` *shudder* – Musa Mar 25 '13 at 19:34
  • Either way, don't use `onreadystatechange` with synchronous requests. Immediately after the `xhr.send(null);` line, just check `xhr.status` and `xhr.readyState` like you want. Also, I wouldn't use synchronous requests unless you have a good reason and understand its use. With asynchronous requests, you have it setup correctly (except for the problem you're having) – Ian Mar 25 '13 at 19:38
  • I wouldn't, ever. I'm just testing out interview question style, coding by hand – bevacqua Mar 25 '13 at 19:41

1 Answers1

8

The problem is the status not available when the readyState is 0/1

You need to reverse the order in your if.

if(xhr.readyState === 4 && xhr.status === 200){

The spec says it should return zero "If the state is UNSENT or OPENED, return 0 and terminate these steps.", but for some reason some browsers do this "Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set." which is what setRequestHeader does. Also your code is weird that you would use it with a synchronous request.

So the issue here is the browser is not returning zero like the spec says so. Changing the order in the if statement prevents the browser from reaching that point since the first check fails.

And the bug on WebKit

epascarello
  • 204,599
  • 20
  • 195
  • 236
  • 1
    Good catch. What I don't understand is why an exception is thrown because wouldn't `xhr.status` just be `undefined` (or whatever) and just not be equal to `200`? I would think it would just fail the condition, not bomb the code – Ian Mar 25 '13 at 19:28
  • Really? Shouldn't it just be `0` (as in Opera) or `undefined` then instead of throwing an exception? – Bergi Mar 25 '13 at 19:31
  • but if you open the connection before you set ready state handler this error does not occur. – Musa Mar 25 '13 at 19:31
  • I wonder if the real problem is because `onreadystatechange` is being used with a synchronous request... – Ian Mar 25 '13 at 19:34
  • no, it wasn't, that didn't seem to change anything, the ordering of checking readyState though fixes it. it's weird that status throws when accessed (`if readyState === 0`), though.. – bevacqua Mar 25 '13 at 19:34
  • 2
    The spec says it should return zero ["If the state is UNSENT or OPENED, return 0 and terminate these steps."](http://www.w3.org/TR/XMLHttpRequest/#the-status-attribute), but for some reason some browsers do this "Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set." which is what [setRequestHeader](http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method) does. Also your code is weird that you would use it with a synchronous request. – epascarello Mar 25 '13 at 19:36
  • @epascarello: Put that in the answer and you'll get another upvote :-) – Bergi Mar 25 '13 at 19:51
  • I just added the webkit bug to the question which was reported in 2010. :) – epascarello Mar 25 '13 at 20:51