AJAX Problems in IE7 (Hangs up browser, returns odd HTTP Request Statuses (12029, 12030, 12031, 12152,12159)

trippy1976
trippy1976 used Ask the Experts™
on
I've been trying to solve this problem all day and think I'm on to the base issue, but cannot figure out the final solution.

In the previous release of my website, my AJAX code supported only a single http_request object (XMLHttpRequest in most browsers, ActiveXObject for IE).  So this meant while I had AJAX on the page if you clicked a button and then clicked another right away, the results from the first button click were orphaned.

SO - I "upgraded" my javascript so that a new http_request object is created each time and then passed to the callbacks so they can monitor their own process and provide feedback in a little progress area.

This works GREAT on Firefox, opera, etc.  It even works fine on IE 6

IE 7 is having all kinds of problems.  First, I thought it was some kind of problem in my PHP code but having read around a little - I believe it's more likely this multiple request objects in IE7.

I found this article:
http://www.missiondata.com/blog/ajax/64/ie7-adds-native-xmlhttprequest-object/

And I think the issue has to do with IE7 and that it now passes the javascript check for firefox, safari, etc. so you end up using the native XMLHttpRequest object to create the request in IE7 vs. the ActiveX thing in IE6.  Which is why I think IE6 works fine while IE7 barfs with those odd error codes.

The codes all have something to do with connection termination, server restart (which isn't happening), etc.  All seem to be connection related.

So at the same time I'm beginning to think this has something to do with IE7's XMLHttpRequest object and it not liking the multiple instantiation stuff - I am under the general impression that the status codes woudl only come back from the server.  So either IE7 is doing something to totally confuse the server or it's "making them up".

Any help or insight or pointers would be greatly, greatly appreciated.
function createRequest()
{
   var http_request = null;
   // I think the issue has something to do with this area of code... (1)
   if (window.XMLHttpRequest) { // Mozilla, Safari,...
      http_request = new XMLHttpRequest();
      if (http_request.overrideMimeType) {
         http_request.overrideMimeType('text/html');
      }
   } else if (window.ActiveXObject) { // IE
      try {
         http_request = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e) {
         try {
            http_request = new ActiveXObject("Microsoft.XMLHTTP");
         } catch (e) {}
      }
   }
   if (http_request == null) {
      alert('Cannot create XMLHTTP instance');
      return false;
   }
   else
   {
   	return http_request;	
   }
}
 
 
 
var reqNum = 0;
function makeRequest(url, parameters, responseDiv, respType) {
	parameters = parameters + "&reqnum=" + reqNum;
	reqNum++;
        // It appears making a new request on each call is fubaring IE7 somehow... (2)
	var http_request = createRequest();
   http_request.open('POST', url, true);
	//Send the proper header information along with the request
	http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	http_request.setRequestHeader("Content-length", parameters.length);
	http_request.setRequestHeader("Connection", "close");   
   
	var statDiv = statusDiv();
	document.body.appendChild(statDiv);
 
   if(respType == "ahah")
   {
		http_request.onreadystatechange = function () {
			ahahCallback(responseDiv, statDiv, http_request);
		}
	}
	else
	{
		http_request.onreadystatechange = function () {
			ajaxCallback(statDiv, http_request, parameters);
		}
	}
   http_request.send(parameters);
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Which content-type declaration do you currently use for the page? Few thoughts that come to mind regarding IE 7, try using the following declaration:

<meta http-equiv=Content-Type content=application/xhtml+xml; charset=UTF-83 />

Try validating your page at http://validator.w3.org/ and resolving any reported issues.
Correction, that should be:
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />

Open in new window

Expert of the Year 2008
Top Expert 2008
Commented:
If it works OK in IE6, try switching the object creation order. You also need to check if the createRequest() function actually returned a valid object.
function createRequest()
{
   var http_request = null;
   // I think the issue has something to do with this area of code... (1)
   
   if (window.ActiveXObject) { // IE
      try {
         http_request = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e) {
         try {
            http_request = new ActiveXObject("Microsoft.XMLHTTP");
         } catch (e) {}
      }
   }
   else if (window.XMLHttpRequest) { // Mozilla, Safari,...
      http_request = new XMLHttpRequest();
      if (http_request.overrideMimeType) {
         http_request.overrideMimeType('text/html');
      }
   } 
   if (http_request == null) {
      alert('Cannot create XMLHTTP instance');
	} 
return http_request;      
}
 
 
 
var reqNum = 0;
function makeRequest(url, parameters, responseDiv, respType) {
      parameters = parameters + "&reqnum=" + reqNum;
      reqNum++;
        // It appears making a new request on each call is fubaring IE7 somehow... (2)
      var http_request = createRequest();
	 
	 if(!http_request)
	 	return false; 
   http_request.open('POST', url, true);
      //Send the proper header information along with the request
      http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      http_request.setRequestHeader("Content-length", parameters.length);
      http_request.setRequestHeader("Connection", "close");   
   
      var statDiv = statusDiv();
      document.body.appendChild(statDiv);
 
   if(respType == "ahah")
   {
            http_request.onreadystatechange = function () {
                  ahahCallback(responseDiv, statDiv, http_request);
            }
      }
      else
      {
            http_request.onreadystatechange = function () {
                  ajaxCallback(statDiv, http_request, parameters);
            }
      }
   http_request.send(parameters);
}

Open in new window

Should you be charging more for IT Services?

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Author

Commented:
switching the order was an intriguing idea.  I did that and it does in fact drive IE7 into the ActiveX object now.

But that does not seem to help much.

It works much of the time but then, and without a pattern I can identify, it starts to act weird.  The issue is basically that you click on a button that should return an ajax result and you get either

- what you expect in return
- Immediate error which is 120**
or
- The request "hangs" for about 60 seconds and you get one of the 121** errors

Then it may go back to working again or it may do that without pattern for the next requests.  The odd thing is that while the ajax request is "hung" you can do NOTHING with that instance of IE.  It's totally dead for network activity.  You can't even hit refresh or click a regular ahref link to go to another page.  You're stuck until you kill the browser instance or it gives you the funky error.

I re-wrote my callback function to update a div for each change in readystate (1-4) and when the problem manifests itself, the readystate never advances beyond 1.

I do test for the http_request object to be valid (see the end of the create function posted where I test for a null).  Or is there more to it than that?

This all worked like clockwork under IE7 before I started creating the http_request object from the createRequest() function.  In the previous version there was a single static http_request object, which got "hijacked" each time a new button is clicked.

This is what has lead me to beleive it's something to do with IE7 hating that createRequest function and then maintaining multiple request objects.  
Expert of the Year 2008
Top Expert 2008

Commented:
Hmmm.... The only last suggestion that comes to mind is to make sure that you free the request object by setting it to null every time a request has completed.
Expert of the Year 2008
Top Expert 2008

Commented:
another thing you can investigate is the behaviour of Msxml2.XMLHTTP vs Microsoft.XMLHTTP. On my last code, priority goes to Msxml2.XMLHTTP. Assuming that is what is what IE is using,  try switching the order so that priority goes to Microsoft.XMLHTTP and monitor its behaviour to see if it is any better.
The content type suggestion and validation I posted are also valid, you are getting WinInet errors http://support.microsoft.com/kb/193625 which IE 7 will display if the server did not respond properly.

The following line from your code above has no effect in IE so you have to ensure that the page returning the response has a valid content-type specified:

if (http_request.overrideMimeType) {
         http_request.overrideMimeType('text/html');
}

The validation will also help find possible issues. This article might also have useful information http://www.javascriptkit.com/dhtmltutors/ajaxgetpost3.shtml

Author

Commented:
Here is what I have changed my code to.

The issue here now is that you cannot make simultaneous AJAX requests.  There are only a handful of operations I really want to do this for so I suppose I could simply add in a second HTTP request object and then check if the first was busy.  If I ever needed a third I could do similar with a third.

It's annoying though as the issue still appears to stem out of the "factory" my createRequest method was acting like.

Now, if I launch two simultaneous Ajax requests, one finishes the other immediately throws 12030 in IE which is what my error was/is in the first place but this time it fires immediately.  Probably because the second request has taken over the object.  Which makes me wonder if somehow IE is allowing the objects to be re-used rather than creating new ones.
// TRY #3
// Set the http_request object in global space... this means only one object at a time though...
var http_request = null;
if (window.XMLHttpRequest) {
	http_request = new XMLHttpRequest();
}
function createRequest()
{
	if (window.XMLHttpRequest) 
	{
		// branch for IE/Windows ActiveX version
	} 
	else if (window.ActiveXObject) 
	{
		alert("here");
		http_request = new ActiveXObject("Microsoft.XMLHTTP");
	}
   
   if (http_request == null) {
      alert('Cannot create XMLHTTP instance');
      return false;
   }
   else
   {
   	return http_request;	
   }
}

Open in new window

Expert of the Year 2008
Top Expert 2008

Commented:
>>Probably because the second request has taken over the object
Exactly. If you are going to make parallel calls simultaneously you need one http_request object per call. Take a step back and consider this:
If you send an asynchronous request to page1.php followed by another request to page2.php, there is NO guarantee that by the time the second request is sent, the first has finished. THAT is the nature of asynchronous requests. So if you had call back function for your first request, when you make the second call and are reusing the http_request object, then you have a callback function for the request to page2.php but you overwrote the reference to the callback of the first request. As soon as you overwrite the previous callback function reference, internally that callback function reference is "marked for deletion". As soon as the garbage collector is invoked, it will see the function marked for deletion and will remove it. So when the first request finishes, it cannot find the original function. Use different request objects for diffent calls.
Since it is working in IE 6 and IE 7 is still compatible with the ActiveX implementation of XMLHTTP how about if you have IE 7 use the ActiveX implementation instead of the new native XmlHttpRequest object, i.e. when you create the object something like:

if (window.XMLHttpRequest && (navigator.appVersion.indexOf("MSIE 7.") == -1))

or set pass false for the third parameter of the open function so the object is no longer asynchronous albeit the first option is better:

http_request.open('POST', url, false);

It looks like this XMLHttpRequest behavior issue in IE 7 is mentioned in several message boards:

http://www.webdeveloper.com/forum/showthread.php?t=177559 
http://www.techtoolblog.com/archives/ie-7-native-xmlhttprequest-not-so-good 
http://support.microsoft.com/kb/183110 (possible explanation related to the limit of concurrent connections)

Author

Commented:
hielo's suggestion at this link accomplished forcing IE7 to the ActiveX and it did not really help:
http://www.experts-exchange.com/M_3082916.html

Good idea though.

And my createRequest() object *was* generating a new http_request object as far as I can tell (see the first post with code snippet)

The issue appears to be that:

- If IE7 is only dealing with a single request object, no matter what kind it is - IE7 works fine.
- If IE7 is dealing with multiple request objects, it exhibits this odd behavior.

The one thing I have to check out still is whether I'm being really careful to null out the request objects in my callback functions and if not - whether it makes any difference.  My assumption has been that you don't really have to worry about garbage collection much with Javascript.  If that's wrong, maybe IE7 is attempting to be helpful in stopping memory leaks by periodically nixing objects.  If it's doing cleanup on an interval, that may explain what's going on.  However, if it is - I'm unsure how to project valid objects that are in active use.

I have been able to reproduce the behavior with only a two connections running which should fall well within IE7's limit (which I think was bumped to 4 vs. IE6's 2 - and in past experience IE "queues" up anything above its concurrent limit for processing when a thread goes free) so I'm thinking it's not concurrent connection related.  I plan to play with the site through a proxy on my network soon to see the full back and forth but hoped I would be able to avoid getting that deep :)

It's interesting in the one forum post about adding false to the end of the open command.  Any idea what difference that makes?

My next version of this will simply instantiate 3 http_request vars and then using an if statement it'll check the status of the objects in sequence to see if they are free.  It's far from ideal, but until I find a definitive answer for IE7's behavior...

I'm going to leave this open so I can post anything I may find but you have both been very helpful and I thank you so much for your ideas and time and will be doling out points soon.  In fairness, I'm looking into this heavily myself and want to post for your review anything I learn as well so for the moment I'm going to let the Q stand open.
Expert of the Year 2008
Top Expert 2008

Commented:
>>And my createRequest() object *was* generating a new http_request object
Yes, but what I was referring to is that if you do this:
http_request = new XMLHttpRequest();

The "real/actual" object is generated by this:
new XMLHttpRequest();

and the variable http_request just holds a reference to that object. If you send an ajax request and before the request finishes you execute this again:
http_request = new XMLHttpRequest();

then http_request now holds a reference to the newly created object but where's the reference to the object of the first request?

>>It's interesting in the one forum post about adding false to the end of the open command.  Any idea what difference that makes?
Try it :) Setting that parameter to true changes the nature of the request FROM ASYNCHRONOUS to SYNCHRONOUS. Basically, as soon as you send your ajax request, javascript does not continue executing until the request is completed. So if it takes the server 10 seconds to complete, the user is "locked out" of the browser for 10 secs. Yep, if you click anywhere else within the browsing area, the browser will not respond to your "click" events.

Author

Commented:
Within the scope of createRequest() http_request is a local variable.

It would make the code more readable perhaps if I used a different name in my createRequest() function than in my makeRequest() function, but a new object is created, returned and should persist to the callback.

I do not have http_request defined as a variable in my global scope at the start of this exercise.  Each new request creates a new request object by using the createRequest() function rather than relying on one or a stable of global request objects.

Now, I've gone back to defining my request object as a global variable which works, but unless you code some logic in (and multiple request objects which you have to carry around whether you need them or not "just in case") to control the use of the request object you can only have a single request running.  
Expert of the Year 2008
Top Expert 2008

Commented:
>>Within the scope of createRequest() http_request is a local variable.
agree.

>>but a new object is created, returned and should persist to the callback.
Not necessarily.

If you do:
var x = createRequest() ;

then x holds a reference to the object created AND returned by createRequest(), but if I then immediately do:
x=5;

then x no longer holds a reference to the http object returned by createRequest().

So if you do this:
var x = createRequest() ;
x.onreadystatechange=function(){if(x.readyState==4){alert("complete");}};
x.open("GET","url.php",true);
//assuming the request takes 1 sec to complete,
x.send(null);
x=5;//this line will be executed before the original http request gets a chance to complete. The original object reference does not exist.

Author

Commented:
Thanks for all the help guys.  Finally at the end of the day I gave up on this and went back to just using the Prototype library.  Even minified it's 10x as big as my home-grown version but it's kept up to date and using it seems to avoid the issues I am having with IE7.

I believe at the end of the day this was probably some obscure programming need I could not identify.  I did not change my server-side stuff at all to make it work with Prototype, but maybe in its guts Prototype is setting content types, etc. correctly as suggested here.

Thanks to you both for your help.  But in the end, I was defeated and went back to using Prototype.  Which is actually not a horrible thing except that currently the only feature of prototype I use is the Ajax stuff.  but it stages me to use scriptalicious, etc. if I want in the future so hey...  what's a little bandwidth right?  It caches anyway and I don't have many / any dialup users.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial