Wednesday, October 3rd, 2007
Optimized Speedy Ajax Code
Dustin Diaz has revisited his seven JavaScript techniques and has updated his XHR-getting-function to be faster, using the Lazy Function Definition Pattern, which ends up looking like:
var asyncRequest = function() {
function handleReadyState(o, callback) {
if (o && o.readyState == 4 && o.status == 200) {
if (callback) {
callback(o);
}
}
}
var getXHR = function() {
var http;
try {
http = new XMLHttpRequest;
getXHR = function() {
return new XMLHttpRequest;
};
}
catch(e) {
var msxml = [
‘MSXML2.XMLHTTP.3.0′,
‘MSXML2.XMLHTTP’,
‘Microsoft.XMLHTTP’
];
for (var i=0, len = msxml.length; i < len; ++i) {
try {
http = new ActiveXObject(msxml[i]);
getXHR = function() {
return new ActiveXObject(msxml[i]);
};
break;
}
catch(e) {}
}
}
return http;
};
return function(method, uri, callback, postData) {
var http = getXHR();
http.open(method, uri, true);
handleReadyState(http, callback);
http.send(postData || null);
return http;
};
}();
[/javascript]





Useless there is little overhead in creating the correct XHR and loading the data over HTTP. There is much more effective result in optimizing the parsing of JSON / XML / etc.
Ignoring the lazy pattern for a minute, why can’t the XHR object be retrieved with simply:
if (window.XMLHttpRequest)
return new window.XMLHttpRequest();
else if (window.ActiveXObject)
return new ActiveXObject(“Microsoft.XMLHTTP”);
This what I have used for a long time without any problems. I don’t understand why frameworks must use a try/catch (try/catchs are usually not very fast, and very annoying with Firebug). And why the different ActiveXObjects, the MS specs seem to make it clear that you don’t need to do that (the code above should always resolve to the latest version)? Is this just case of code that has been copied time and time again without question?
In fact, try catch blocks have been shown to be extremely slow on certain browsers (the iPhone’s version of Safari for example).
Neglecting the try/catch and micro-optimization accusations for a moment, I started with a similar chunk of code copied from Dojo (dojo/src/hostenv_browser.js) and applied the lazy function pattern as well, except that I managed to refactor some of the logic by using an array of functions.
https://cixar.com/tracs/javascript/browser/trunk/modules.js?rev=136#L1433
Microsoft.XMLHTTP will always get versions up to 3. Checking for Msxml2.XMLHTTP is a waste of time since it always gets version 3. Msxml2.XMLHTTP.4.0 and 6.0 are not returned by Microsoft.XMLHTTP. 6.0 was released with Vista.
http://www.telerik.com/documents/AJAX%20Page/Ajax-Part1.pdf page 3
This is a great exercise for the Lazy Function Declaration pattern, if only for its educational value, or because try/catch is expensive.
This sample is pretty green though (in the arboreal sense). It doesn’t take into account status eccentricities of various browsers over various protocols. For example KHTML doesn’t mask the 304 cache hit status (other browsers handle it and change it to a 200). Also in some browsers, under various conditions, you’ll get “undefined” or “0” for OK.
[quote] I don’t understand why frameworks must use a try/catch (try/catchs are usually not very fast, and very annoying with Firebug).[/quote]
Disable ActiveX and see what happens.
@Eric
Considering your argument about disabling ActiveX, I’d still narrow the try/catch block down to the ActiveX control construction.
But you simply don’t need to try/catch anything, if ActiveX is disabled and window.XMLHttpRequest doesn’t exist you can perfectly verify that you got what you need without “trying”.
@Rogier
Totally agree with you. Object detection is always the way to go.
My comment is basically why you probably would want to use the around the ActiveX. Talked about the error a long time ago on my blog here: http://radio.javaranch.com/pascarello/2006/02/07/1139345471027.html
Eric
Am I the only one, who prefers higher level “complexity”? I know that try-catch is slow or that foreach-like syntax is bad for performance, but I prefer to let these things handled by frameworks (either inhouse, or for pet projects prototype.js) – hardware components and browsers are getting faster/better by the day (by the year, sry:)), but code reusability and readibility will not improve by itself.
@deadcabbit What you say is true, but only if the effect is (near) transparent. In the case of try-catch it isn’t on the iPhone. It literally takes seconds to pass try catch blocks.
As others have already pointed out in the article comments, this code doesn’t work at all, because readyState is only checked once before sending the request (instead of polling or putting it on onreadystatechange event)