“I put a function in your function, so you can process while you process!”

Maybe you’ve seen this before:

public function foo():void{
    // a function in a function?
    var onLoad:Function = function(e:Event):void{
        addChild(loader);
    }
    var loader:Loader = new Loader();
    loader.addEventListener(Event.COMPLETE, onLoad);
    loader.load(new URLRequest("someSwf.swf"));
}

It appears that the developer in question has written a function inside of a function. They were at least kind enough to assign it to a variable. They could have created one that is completely anonymous.

So, is there something wrong with this? In ActionScript 3 the answer is it kind of depends.

There are legitimate reasons why a developer might wish to do this, and they involve coding around a weakness in the Event model. The most interesting of these reasons is that this style represents the only way you can get away with early binding of variables. In doing so however, it’s easy to create objects that can never be eligible for garbage collection. Notice in the above example that the developer never removed the listener! In the examples to follow, I will exercise due diligence and remove them explicitly.

As an example, I’ve seen developers write code like this, thinking that the value of count will be “fixed” in the event handler to what it was when the handler was assigned:

private var count:int = 0;
private const MAX_COUNT:int = 10;
 
public function foo():void{
    while(count < MAX_COUNT){
 
        var onLoad:Function = function(e:Event):void{
            // offset the loader's x value by width * the number of images loaded.
            loader.x = loader.width * count;
            addChild(loader);
            e.currentTarget.removeEventListener(Event.COMPLETE, onLoad);
        }
        var loader:Loader = new Loader();
        loader.addEventListener(Event.COMPLETE, onLoad);
        loader.load(new URLRequest("someSwf.swf"));
    }
}

Only to discover that the value contained in count is not at all bound at the time the listener is added. They find that *all* their images end up at content.width * 10. Their next impulse is to “find a way to pass the current count to the event handler”, but you cannot do that. This:

// trying in vain to pass 'count' to the listener...
 loader.addEventListener(Event.COMPLETE, onLoad, count);

Is simply not valid AS3 code. There are two ways around this. My preferred method is to keep your real count at the class level, and to increment it in onLoad rather than to rely on the foo() function, and to never use a nested inner function. This gives the class a very plain set of signatures that can be read and interpreted in a list, and whose namespaces can be changed as needed to allow or deny access from both the outside and the inside. I find it worth the extra few lines of code and scrolling in the short term to have something easier to read in the long term.

 
private var count:int = 0;
private const MAX_COUNT:int = 10;
 
public function foo():void{
    // remove the count from foo, and use a local variable.
    var i:int = 0;
    while(i < MAX_COUNT){
        var loader:Loader = new Loader();
        loader.addEventListener(Event.COMPLETE, onLoad);
        loader.load(new URLRequest("someSwf.swf"));
        i++;
    }
}
private function onLoad(e:Event):void{
    var content:DisplayObject = e.currentTarget.content as DisplayObject;    
    content.x = content.width * count;
    this.addChild(content);    
    // increment the count here.
    count++;
    e.currentTarget.removeEventListener(Event.COMPLETE, onLoad);
}

So as you can see, there’s no need to use the listener-within-a-function. Why then, does it remain tempting for people? Aside from the fact that it requires less scrolling around the page and you can just write out the function in a place where you can see everything you’re thinking about, there is a special way to do this that allows you to use early binding. Get ready, because we’re about to take it another level down the rabbit hole.

public function foo():void{
    // a function in a function?
   for(count ; count < 10 ; count++){
        var onLoadHandler:Function = function():Function{
            var c:int = count;
            // with another function in a function inside of it???
            var handler:Function = function(e:Event):void{
                var count:int = c;
                var displayObject:DisplayObject = e.currentTarget.content as DisplayObject;
                displayObject.x = displayObject.width * count;
                addChild(loader);
 
                // IMPORTANT: Remember to remove your listener!
                e.currentTarget.removeEventListener(Event.COMPLETE, handler);
            }        
            return handler;
        }
        var loader:Loader = new Loader();
        // Call the function onLoadHandler to get the *real* function that is used as the listener as a return value
        loader.addEventListener(Event.COMPLETE, onLoadHandler());
        loader.load(new URLRequest("someSwf.swf"));
    }
 
}

Once you start nesting functions this deeply and returning the inner functions as values to be used, you’re stepping across the line of Object Oriented Programming into Functional Programming, though it’s a half-baby-step in this case. You’ll notice in this instance that the value of “count” in the innermost function is bound to the value of “count” at the class level as it existed when the originating function was declared in the for loop because of the intermediary “c” variable. This works because of the acrobatics involved in working across so many scopes in a single function call. I’ll come out and admit that while it can be initially confusing to see so many functions nested, it *is* convenient not to have to declare extra class level variables or scroll around looking for other functions in the code. You may wonder then why I prefer not to use them.

  • Function calls are processor intensive. Inside a long loop, calling so many functions adds considerable overhead.
  • It obscures your API. These inner-functions and variables never appear on a class diagram.
  • You can not directly touch these functions from anywhere except the scope in which you create them.

1. In my post about Array Optimization you’ll note that there’s nothing particularly intensive happening in those loops. Imagine however, that there *is* something to do in all those locations. There are no empty array locations, and for each item in the array there is a function call. Now imagine that there are two or three function calls. In fact don’t just imagine it, benchmark it yourself. You’ll find that function calls simply consume a great deal more resources than code written inline.

2. It’s one thing to write OOP code with intentional encapsulation such that objects don’t know much about each other. It’s another thing to write it such that the objects don’t know much about themselves. That’s essentially what’s happening when you rely on multiply nested function-scoped functions. It makes the code harder to understand at a glance from a high level. “I know that there’s a function that says to load images, but I don’t see where that’s being handled and I cannot override it in a subclass without overriding the loading function too.

3. This is the really big one, and the one that has the most room to trip you up in weird ways (hence the “pitfall” categorization of this entry). While Flash does offer you the option to add an event listener as a “weak reference” for aid in garbage collection, that doesn’t mean that once the object is eligible for gc that the object has ceased to exist, or that it will stop dispatching events. Far from it! With asynchronous events such as Event.COMPLETE (in the context of loading external content), TimerEvent (all of them), and even the synchronous Event.ENTER_FRAME, you’ll find that these events still dispatch, even if the references are weak, and even if the dispatching object is eligible for garbage collection. How do you remove an event listener that you don’t have any access to outside the scope in which it’s added? The only answer is “you have to do it inside the handler itself” which may not be practical or even possible under many circumstances. It is possible in the context of the Loader class (as demonstrated above) but the waters become murkier with ENTER_FRAME or TimerEvent(s), as the function now needs to undertake the responsibility of checking whether it should somehow remove itself from its dispatcher every time it executes, and possibly aborting execution if it should be removed.. This of course adds CPU overhead to the code, and if the event is dispatched frequently represents a large amount of needless processing.

You’re probably wondering why The Horseman would even tell people that this is possible if he’s so reluctant to use these nested functions himself. The simple answer is that people are going to do this anyway. No matter my own personal preference, there will always be coders whose first inclination is to use functions inside functions, and that’s fine. There’s a whole paradigm based around passing functions and relying ONLY on function scope, in fact! The problem comes when people who don’t know the risks and the trade-offs are careless about using them, particularly in an otherwise Object Oriented environment. Consider this to be a primer on those risks and the trade-offs, at least in the context of ActionScript 3. There are more to consider, but that’s for another time.

Tags: , , , ,

  1. No comments yet.