Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anonymous functions as event handlers in Action Script - good or bad?

I came to AS3 from JS world, and I should confess that anonymous functions are my weakness. I tend to use them everywhere. Now, coming to AS3 I've heard and read in lots of places, that AS and Flash are enormously bad at handling garbage collection, that one should empty, dispose and remove all event handlers and objects manually to avoid weird and unexplainable memory leaks and crashes. Not sure what part of this is true, but I would like to follow best practices right from the beginning.

So my question would be - how bad is idea of using anonymous functions as event handlers? Consider for example a code like this:

addEventListener(Event.ENTER_FRAME, function() : void {
    controls.elapsed = stream.time;
});

contorls.elapsed is the setter, which apart from setting current play time for video player, updates the whole UI, and stream is NetStream object, which streams the actual video.

There are lot's of other places where anonymous function may make code cleaner and more intuitive. Check the following code for simple fade-in effect for the control bar:

public function showControls() : void
    {
        var self:Controls = this;

        if (!visible) {
            visible = true;
            fadeTimer = new Timer(30, 10);
            fadeTimer.addEventListener(TimerEvent.TIMER, function() : void {
                self.alpha += 0.1;
            });
            fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function() : void {
                self.alpha = 1;
            });
            fadeTimer.start();
        }
    }

I totally like how it looks and fits into the code, but I'm concerned about leaks. While Event.ENTER_FRAME handler probably would never become harmful in this form, what about timer listeners. Should I remove those listeners manually, or they will be removed automatically, as soon as I set fadeTimer = null ? Is it possible to remove listeners with anonymous functions properly at all?

like image 965
jayarjo Avatar asked Dec 12 '10 09:12

jayarjo


2 Answers

Just noticed this post -- there are a couple things that might be of use to you. One is arguments.callee (which is a reference to the current function you're in). This is useful for removing references in anonymous functions. Also, it could be noted that you could use weak references in your addEventListener code -- however, this won't work for variables that are anonymous, as they'd get GC'd pretty much immediately. For simplicity sake I rewrote your code like this: (should work -- haven't tested)

private function showControls() : void {

    if (visible) {
        return;
    }

    var self:DisplayObject = this;

    var fadeTimer= new Timer(30,10);
    var handler = function(e:Event) {

        switch (e.type) {

            // timer complete
            case TimerEvent.TIMER_COMPLETE:

                // remove references to this anonymous function -- for garbage collection
                fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, arguments.callee);
                fadeTimer.removeEventListener(TimerEvent.TIMER, arguments.callee);

                // break out
                return self.alpha = 1;

            // timer
            case TimerEvent.TIMER:
                return self.alpha += 0.1;

        }
    }

    fadeTimer.addEventListener(TimerEvent.TIMER, handler);
    fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, handler);
    fadeTimer.start();

}
like image 116
ansiart Avatar answered Oct 14 '22 18:10

ansiart


I would do it something like this. And, be sure to use dispose() when you want to make sure to clear the timer if interrupting.

private function showControls() : void
{
    if(_isVisible)
        return;

    // start you control here
    _fadeTimer = new Timer(30, 10);
    _fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
    _fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
    _fadeTimer.start();
}

private function updateFade(event : TimerEvent) : void
{
    // update fade here
}

private function updateFadeComplete(event : TimerEvent) : void
{
    dispose();
}


private function dispose() : void
{
    if(_fadeTimer)
    {
        _fadeTimer.stop();
        _fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
        _fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
        _fadeTimer = null;
    }
}
like image 42
Mattias Avatar answered Oct 14 '22 18:10

Mattias