/customers/iconara.net/iconara.net/httpd.www/blog/wp-content/plugins/wp-super-cache/wp-cache-phase1.php Iconara » Wonderful closures, evil closures!

Wonderful closures, evil closures!

One of the things that makes JavaScript (and ActionScript, by association) a wonderful language to program in is it’s support for closures and higher order functions. It’s a powerful feature, and it can greatly simplify all the asynchronous programming that you do in web applications. It also makes it possible to do advanced but useful abstractions of things like iteration and transformations of datastructures.

However, there is a downside to this, and that is what the second half of the title refers to. It’s very easy to fall into a hidden trap: closures are a source for hidden memory leaks.

Just to catch up, and make sure we’re talking about the same thing, here is a short introduction to closures:

function changeIt( it ) {
    var one = 1;
    var two = 2;

var service = new AsyncService();

service.onFinished = function( ) {
    it.value = one + two;
}

service.start();

}

The when the function changeIt is called the variables that are defined inside it is in the scope of the function, as are the arguments to the function. The function that is assigned to service.onFinished is also in the same scope. The body of that inner function is not executed with the rest of the function, but at another time, when the service calls it’s onFinished handler, but the inner function still knows about the rest of the variables in the scope (one, two, it and service) it is closed over the containing scope, and is called a closure.

Closures are nice in a number of ways; they can make code that does asynchronous code easier to understand by having the code that starts the call and the code that is run when the call returns in the same place (as above). And it can also make those situations easier to code, since the code that is run when the asynchronous call returns has access to the same scope that the code that started the call, thus making asynchronous calls more like regular synchronous calls.

You can also use them to create one-off event handlers, like in this code:

button.addListener(
    Button.CLICK_EVENT,
    function( ) { doTheThing(); } // anonymous closure
);

But what’s wrong with this?

In the example above, the button object has a reference to the anonymous closure, which is fine, it’s supposed to have one, how else would it know what to do when it was clicked? But the trouble is that the anonymous closure also has a reference to the button. This is called a circular reference, ad they can be a bit of a bummer. This one is quite simple, only two objects referencing each other, but there is no end to how long a circular reference can be.

Circular references are usually handled by the garbage collector (the thing that deallocates unused objects in the JavaScript/ActionScript runtime) as a side effect. A mark-and-sweep garbage collector, like the one used in the ActionScript runtime as of Flash 8, and most (if not all) web browsers starts by marking all objects in memory, then it unmarks every object it finds by traversing the object graphs rooted in the accessible variables of the running program. Everything that is still marked after this is garbage and thrown away. So even if there is a circular reference amongst the marked objects, it’s thrown away, as it should be.

But don’t think that I’ve cried wolf when there’s none! Now, consider this example (this is ActionScript, but mostly translatable to JavaScript):

public function doTheThing( ) {
    var self = this;

var service = AsyncServices.getService();

service.onFinished = function( ) {
    self.doAnotherThing();
}

service.start();

}

There are two differences to the first example: first we store a reference to this in a variable called self, this is to have the current object available in the onFinished handler (since a closure is a function it has it’s own this that refers to the function itself). The other difference is that the asynchronous service we are using is aquired through a class method, and in this case we’ll assume that that’s because the service objects are pooled and reused.

With this in mind, consider what happens: the service object can live for quite long, as it’s likely never thrown away until the application quits, and until it’s reused it is going to have a reference to the object that used it, which means that that object will not be deallocated until then.

There are a number of objects in the ActionScript runtime that are never deallocated either because they are global or that they don’t belong to the runtime but are native objects (movie clips are a good example, as is the global object Stage and Mouse). In JavaScript, DOM objects aren’t JavaScript objects, which means that they are not deallocated by the JavaScript garbage collector, they will live as long as the DOM object lives, and in an Ajax-application, that can be a long time, in IE it appears that it’s sometimes even until the browser quits, google for memory leak, javascript and dom and see for yourself.

Anyway, watch out, don’t assign closures that have access to large datastructures to the wrong object. When you can’t access this without assigning it to a temporary variable, you should think twice about what you are doing. In many cases it might be allright, but in some it can be bad. Also try to unregister from callbacks and event handlers when you are done with them.

Reading this post again, I realise it’s a load of FUD. Closures are not a problem in ActionScript (even though they are in Ajax). I hope it has made what a closure is more clear to you, and that you can disregard the warnings that are not really relevant. Closures can hog memory in ActionScript, but I no longer think it’s a big issue.

One Response to “Wonderful closures, evil closures!”

  1. Sawan Says:

    I wrote a blog on a similar subject, but more related to what the ‘this’ object in function Object is and their scopes. Leave comments if any. http://abeginner.blogspot.com/2008/02/flex-mxml-functions-and-nested.html

Leave a Reply