|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionMonths ago, a friend of mine, who is a also a consultant and trainer, told me that one of his customers met a problem. IE will be stuck when too many connections have been set up in the page at the same time. The problem is becoming more and more popular since AJAX technology has been widely used these days. When an AJAX application is composed of smaller ones - that we call it "mash up" - the problem will be likely to occur. It's a bug in Internet Explorer. When you make a lot of AJAX calls, the browser keeps all the requests in a queue and executes two at a time. So, if you click on something to try to navigate to anthoer page, the browser has to wait for running calls to complete before it can take another one. The bug is quite serious in IE 6 and unfortunately, it still exists in IE 7. Manage the requests programicallyThe solution is simple. We should maintain the queue ourselves and send requests to the browser's queue from our queue one by one. Thus I wrote a queue to manage the requests. It's really a piece of cake: if (!window.Global)
{
window.Global = new Object();
}
Global._ConnectionManager = function()
{
this._requestDelegateQueue = new Array();
this._requestInProgress = 0;
this._maxConcurrentRequest = 2;
}
Global._ConnectionManager.prototype =
{
enqueueRequestDelegate : function(requestDelegate)
{
this._requestDelegateQueue.push(requestDelegate);
this._request();
},
next : function()
{
this._requestInProgress --;
this._request();
},
_request : function()
{
if (this._requestDelegateQueue.length <= 0) return;
if (this._requestInProgress >= this._maxConcurrentRequest) return;
this._requestInProgress ++;
var requestDelegate = this._requestDelegateQueue.shift();
requestDelegate.call(null);
}
}
Global.ConnectionManager = new Global._ConnectionManager();
I build the component names For example, if we are using Prototype framework to make ten AJAX calls continuously: function requestWithoutQueue()
{
for (var i = 0; i < 10; i++)
{
new Ajax.Request(
url,
{
method: 'post',
onComplete: callback
});
}
}
function callback(xmlHttpRequest)
{
// do sth.
}
And we'll use the function requestWithQueue()
{
for (var i = 0; i < 10; i++)
{
var requestDelegate = function()
{
new Ajax.Request(
url,
{
method: 'post',
onComplete: callback,
onFailure: Global.ConnectionManager.next,
onException: Global.ConnectionManager.next
});
}
Global.ConnectionManager.enqueueRequestDelegate(requestDelegate);
}
}
function callback(xmlHttpRequest)
{
// do sth.
Global.ConnectionManager.next();
}
Please note that we assign the I send the file to my friend and serveral days later he told me that his customer said the component is hard to use. I agreed. It's really verbose and error prone. Apparently the Build a fake XMLHttpRequest typeI got an idea after days of thinking. That will be perfect if we can use another component to replace the native The solution is much easier than I thought before and now I'm going to show you how to build it. The first thing we should do is to keep the native window._progIDs = [ 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP' ];
if (!window.XMLHttpRequest)
{
window.XMLHttpRequest = function()
{
for (var i = 0; i < window._progIDs.length; i++)
{
try
{
var xmlHttp = new _originaActiveXObject(window._progIDs[i]);
return xmlHttp;
}
catch (ex) {}
}
return null;
}
}
if (window.ActiveXObject)
{
window._originalActiveXObject = window.ActiveXObject;
window.ActiveXObject = function(id)
{
id = id.toUpperCase();
for (var i = 0; i < window._progIDs.length; i++)
{
if (id === window._progIDs[i].toUpperCase())
{
return new XMLHttpRequest();
}
}
return new _originaActiveXObject(id);
}
}
window._originalXMLHttpRequest = window.XMLHttpRequest;
And then, we should create a new class to replace the native window.XMLHttpRequest = function()
{
this._xmlHttpRequest = new _originalXMLHttpRequest();
this.readyState = this._xmlHttpRequest.readyState;
this._xmlHttpRequest.onreadystatechange = this._createDelegate(this, this._internalOnReadyStateChange);
}
window.XMLHttpRequest.prototype =
{
open : function(method, url, async)
{
this._xmlHttpRequest.open(method, url, async);
this.readyState = this._xmlHttpRequest.readyState;
},
setRequestHeader : function(header, value)
{
this._xmlHttpRequest.setRequestHeader(header, value);
},
getResponseHeader : function(header)
{
return this._xmlHttpRequest.getResponseHeader(header);
},
getAllResponseHeaders : function()
{
return this._xmlHttpRequest.getAllResponseHeaders();
},
abort : function()
{
this._xmlHttpRequest.abort();
},
_createDelegate : function(instance, method)
{
return function()
{
return method.apply(instance, arguments);
}
},
_internalOnReadyStateChange : function()
{
// ...
},
send : function(body)
{
// ...
}
}
The key points are the implementations of the send : function(body)
{
var requestDelegate = this._createDelegate(
this,
function()
{
this._xmlHttpRequest.send(body);
this.readyState = this._xmlHttpRequest.readyState;
});
Global.ConnectionManager.enqueueRequestDelegate(requestDelegate);
},
We assign the _internalOnReadyStateChange : function()
{
var xmlHttpRequest = this._xmlHttpRequest;
try
{
this.readyState = xmlHttpRequest.readyState;
this.responseText = xmlHttpRequest.responseText;
this.responseXML = xmlHttpRequest.responseXML;
this.statusText = xmlHttpRequest.statusText;
this.status = xmlHttpRequest.status;
}
catch(e){}
if (4 == this.readyState)
{
Global.ConnectionManager.next();
}
if (this.onreadystatechange)
{
this.onreadystatechange.call(null);
}
},
We have tried our best to let the new component behaves as the same as the native How to useAnd now, we can easily reference the js file in the page to solve the problem when the user browses the page using IE. <!--[if IE]>
<script type="text/javascript" src="ConnectionManager.js"></script>
<script type="text/javascript" src="MyXMLHttpRequest.js"></script>
<![endif]-->
I sent the script file to my friend. It seems that his customer is quite pleased with this solution.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||