.

Monday, April 24, 2006

another way to prevent memory leak in IE when using XMLHTTPRequest #

This post is a follow up of my former leak-free XMLHttpRequest Wrapper post. So if you have not gone through it, read it to get an initial idea.

By the way, I'm improving XHRequest of sardalya (and many other objects), a brand new release will come before this weekend.

Anyway, I was seriously thinking that somehting was wrong about the XHRequest._cleanup method:

_this._cleanup=function()
{
if(!this._xhr)return;
delete this._xhr.onreadystatechange;
this._xhr=null;
};


So each time I use a new get request, I was re-creating a new XMLHttpRequest member for (this._xhr), since I was setting the object to null in the last line.

Removing the last line would theoretically be sufficient. But I was secretly wanting to set this._xhr.onreadystatechange=null;

_this._cleanup=function()
{
/*if you try to set the event handler to null to break the circular reference.
This will not work in IE, and it will throw a type mistmatch exception.*/

this._xhr.onreadystatechange=null;
};
which I could not. Since IE was generating a type mistmatch error if I try.

I'll come to this point later. But just before that let me introduce a small test script explaining closures and scopes:

/body\
/script\
var Constant={
NULL:function()
{
/*
* This method does not close over testMember.
* testMember is out of scope.
* This will just alert "undefined".
*/
alert(typeof testMember);
}
}
var TestObject=function()
{
this.initialize();
};

TestObject.prototype.initialize=function()
{
var testMember=document.getElementById("TestLayer");
var self=this;

if(arguments.length)
{
testMember.onclick=Constant.NULL;
}
else
{
testMember.onclick=function()
{
/*
* Here, this anonymous function closes over testMember;
* testMember is within the scope of it.

* this will alert [Object].
*/
alert(typeof testMember);
self.initialize(true);
};
}
};

window.onload=function(evt)
{
var to=new TestObject();
};
//script\
/div id="TestLayer"\Test Layer//div\
//body\

When you run the script on body load, TestObject.initialize() method is called for the first time. So when you click on TestLayer it will alert [Object] and then assings a new function to the onclick. Therefore when you click TestLayer for the second time it will alert undefined.

Do you see where I am getting?

var Constant=
{
...,
NULL:function{}
};

...

_this._cleanup=function()
{
this._xhr.onreadystatechange=Constant.NULL;
};

Just one line of code!

This syntax completely removes the closure, whereas it does not destroy this._xhr, so you do not have the memory overhead of recrating a new XMLHttpRequest object (since this._xhr is not set to null, we just need re-assign this._xhr.onreadystatechange).

It is better than the first one. It is also cooler, I guess ;)

Describing it with a code sample would be more clear I guess:

...
_this.getObject=function()
{
if(this._xhr)return this._xhr;
else if(window.XMLHttpRequest)return new XMLHttpRequest();
else if(window.ActiveXObject)return new ActiveXObject("Microsoft.XMLHTTP");
else return null;
};

In the former version of the script, we were always creating a new XMLHttpRequest object because we were assigning this._xhr=null; after each cleanup. Now, we do not set it to null. So the code-flow will enter the highlihted part only once (in object's creation). Only the first line will execute thereafter, which is better in terms of performance (creating a new XMLHttpRequest at each HTTP GET request to server will be a costly operation, especially if you are creating and AJAX-heavy application and you are using many many of those objects around).

NILL is an object

Secondly, this approach makes NULL an object!. I mean NULL is now an object, just as nill is an object in Ruby.

Most of you have seen "null is null or not an object" error message at least once in your life time ;)

null is not an object but NULL is. So we may even extend it a bit:

Constant.NULL._isNull=true;
Constant.NULL.prototype.toString=function(){return "NULL"};
Constant.NULL.prototype.equals=function(someObject)
{
//return someObject===null||(someObject&&someObject._isNull);
return (someObject&&someObject._isNull);
}
Well it makes things overcomplicated I know (why not use myvar===null instead of Constant.NULL.equals(myVar) -- it's impractical).

I'm just thinking aloud though.

But I'm sure ruby guys (and gals) around will like this approach.

Cheers.

 bu yaziyi sevdin mi?  hemen una ekle!
 


0 Coments


Post a Comment

Links to this post:


Create a Link

<< Home




Recent Posts

RSS

RSS register icon

Other Blogs

Various

Sponsor

Profile Information

Browser I Suggest

Sponsor

Dikkatimi Çekenler