After creating
a simple leak free defensive AJAX wrapper, and
tweaking a bit to more effectively handle leaks, I found out that under certain (really uncommon, infrequent and abnormal) situations the object can still leak a little bit of memory.
So I decided to attach an
onunload handler to the constructor and get rid of the leaks for good.
In addition, I enhanced the object further. It will be available to the rest of the world when I release the new version of
sardalya. But for you lucky guys out there, I'm pasting entire code below.
/** XHRequest ----- **/
function XHRequest()
{
this._fields=[];
this._values=[];
this._initialized=false;
this.init();
}
_this=XHRequest.prototype;
_this.removeAllFields=function()
{
this._fields.length=0;
this._values.length=0;
};
_this.addField=function(strField,strValue)
{
this._fields.push(strField);
this._values.push(TextFormatter.escape(strValue));
};
_this.post=function(strURL,_blnSync)
{
if(!this._xhr)
{
return;
}
if(!_blnSync)
{
_blnSync=false;
}
var uq=this._generateURL(strURL);
this._xhr.open("POST",uq.url,!_blnSync);
this._setRequestHeaders();
this._xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
this._xhr.send(""+uq.query/*null -- Opera does not like null.*/);
this._postProcess(_blnSync);
};
_this.postSynchronized=function(strURL)
{
this.post(strURL,true);
};
_this.get=function(strURL,_blnSync)
{
if(!this._xhr)
{
return null;
}
if(!_blnSync)
{
_blnSync=false;
}
var uq=this._generateURL(strURL);
this._xhr.open("GET",uq.url+"&"+uq.query,!_blnSync);
this._setRequestHeaders();
this._xhr.send("");
this._postProcess(_blnSync);
};
_this._postProcess=function(blnSync)
{
if(blnSync)
{
/*
* note that 99.99% of the time you'll make async requests
* so this method will not be executed at all.
*/
this._processAsyncRequest();
}
else
{
AjaxController.incrementCount();
}
};
_this._processAsyncRequest=function()
{
var obj=this._xhr;
if(obj.status==200||obj.status==304)
{
this.oncomplete(obj.responseText,obj.responseXML);
}
else
{
this.onerror(obj.status,obj.statusText);
}
this._cleanup();
};
_this.getSynchronized=function(strURL)
{
this.get(strURL,true);
};
_this.getObject=function()
{
var request=null;
var arProgID=null;
if(this._xhr)
{
request=this._xhr;
}
else if(window.XMLHttpRequest)
{
request=new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
try
{
request=new ActiveXObject("Msxml2.XMLHTTP");
}
catch(othermicrosoft)
{
try
{
request=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (failed)
{
arProgID=[
"Msxml2.XMLHTTP.7.0",
"Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0"];
for(var i=0;i<arProgID.length;i++)
{
try
{
request=new ActiveXObject(arProgID[i]);
break;
}
catch(ignore)
{
}
}
}
}
}
return request;
};
_this.finalize=function()
{
if(!this._xhr)
{
return;
}
this._cleanup();
this._xhr=null;
};
_this.init=function()
{
var sourceElement=this;
var obj=null;
var cleanme=null;
this._xhr=this.getObject();
if(!this._xhr)
{
return;
}
if(!this._initialized)
{
/*
* Since we cannot add onreadystatechange event
* to the EventRegistry using EventHandler.addEventListener
* we need a special cleanup process to take care
* of circular references.
*
* If for some reason this._xhr still remains on memory
* before window unload; then this method will take
* care of it.
*/
cleanme=function(evt)
{
/* finalize the sourceelement */
sourceElement.finalize();
/* detach cleanup event. */
_.unchain(window,"unload",cleanme);
/* Remove the final remaining references */
/*
* The three lines below are not necessary.
* They are just for defensive coding.
*/
cleanme=null;
obj=null;
sourceElement=null;
};
_.chain(window,"unload",cleanme,true);
}
this._xhr.onreadystatechange=function()
{
obj=sourceElement.getObject();
if(obj.readyState==4)
{
/*now that the request is completed*/
AjaxController.decrementCount();
/*
* 404: file not found.
* 200: OK.
* 304: reading from cache.
*/
if(obj.status==200||obj.status==304)
{
sourceElement.oncomplete(obj.responseText,obj.responseXML);
}
else
{
sourceElement.onerror(obj.status,obj.statusText);
}
/*to prevent IE memory leak due to circular COM referencing.*/
sourceElement._cleanup();
}
};
this._initialized=true;
};
_this._setRequestHeaders=function()
{
this._xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");
this._xhr.setRequestHeader("X-Sardalya-Version",Sardalya.version);
this._xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");
this._xhr.setRequestHeader("Accept",
"text/javascript, text/html, application/xml, text/xml, */*");
};
_this.abort=function()
{
/*now that the request is completed*/
if(AjaxController.getActiveThreadCount()>0)
{
AjaxController.decrementCount();
}
if(this._xhr)
{
this._xhr.abort();
}
this._cleanup();
};
_this.oncomplete=function(strResponseText,objResponseXML)
{
alert("Completed everything.");
};
_this.onerror=function(intStatus,strStatusText)
{
alert("An error occured but my owner is too lazy to handle it.");
};
_this._cleanup=function()
{
/* release circular reference to solve the memory leak proble in IE. */
this._xhr.onreadystatechange=_.nill();
};
_this._generateURL=function(strURL)
{
var len=this._fields.length;
var append="";
if(len>0)
{
for(var i=0;i<len;i++)
{
append+="&"+this._fields[i]+"="+this._values[i];
}
}
if(append==="")
{
append="&";
}
return {url:(strURL+"?rnd="+Math.random()),query:append.substring(1)};
};
Hope that helps.
bu yaziyi sevdin mi?
hemen
una ekle!