Question

I have a strange issue! I am trying to remove an event listener on a FileReference object by calling a function, but it seems not to be removed, and I do not understand why.

Here is the code:

private function clearFileUploadListeners(file:FileReference, index:String):void {
    var dispatchEvent:Function = function(event:Event):void {
        dispatch(event.type, event, index);
    };

    file.removeEventListener(Event.COMPLETE, dispatchEvent);
    var bool:Boolean = file.hasEventListener(Event.COMPLETE);
    if (bool)
        trace("ERROR");
}

When I run this code, the trace actually happens. I don't understand why this boolean returns true, when I just tried to remove the eventListener just above! I guess I am probably doing something really stupid because it seems like a strange error.

I hope someone can please help me on this issue.

EDIT:

I believe it has to do with the fact that the dispatchEvent function is defined inside another function when I add the listener:

private function upload(file:FileReference, index:String):void {
    var dispatchEvent:Function = function(event:Event):void {
        dispatch(event.type, event, index);
    };

    file.addEventListener(Event.COMPLETE, dispatchEvent);
}

The problem is that I need to access this "index" variable from the listener, and I can't set it as a global variable as each file has it's own index and it's a burden if I have to extend each event class to keep track of the index (Event, ProgressEvent, ..). I hope someone can please help me on this.

EDIT2:

I actually found a temporary solution, I am not sure if it is the best! I put my removeListener method actually inside the upload method, but made it a variable. As AS3 allows dynamic object, I attached this method to one of my object, and so I just call the reference to the method when necessary. The event is actually removed. Is this a good solution please?

Thank you very much, Rudy

Was it helpful?

Solution

You're right, it has to do with the fact that you're defining a function inside another function, then using it to handle events.

Each time the function upload is called, it creates a new closure, and assigns a reference to it to the dispatchEvent variable, which is then passed to the addEventListener class. So each time upload is called, it is using a new, different closure in the call to addEventListener. Similarly, in the clearFileUploadListeners function, a new closure is being created on each call (which happens to have the same code each time, but isn't the same function object). The call to removeEventListener does nothing if the given callback has not been added as an event listener for the given event, which is the case here.

To solve your problem, you need to store a reference to the closure that you pass to the addEventListener function. This way, you can get a reference to the same closure that was added when you need to remove it later in clearFileUploadListeners.

You can try something along the lines of the following code (untested):

import flash.utils.Dictionary;

var callbackRegistry:* = new Dictionary();


private function upload(file:FileReference, index:String):void {
    var dispatchEvent:Function = generateFileUploadCompleteCallback();

    callbackRegistry[file] = dispatchEvent;

    file.addEventListener(Event.COMPLETE, dispatchEvent);
}

private function clearFileUploadListeners(file:FileReference, index:String):void {
    var dispatchEvent:Function = callbackRegistry[file];
    callbackRegistry[file] = null;

    file.removeEventListener(Event.COMPLETE, dispatchEvent);

    var bool:Boolean = file.hasEventListener(Event.COMPLETE);
    if (bool)
        trace("ERROR");
    else
        trace("YAY, ALL OK!");
}

private function generateFileUploadCompleteCallback(index:String):Function {
    return function(event:Event):void {
        dispatch(event.type, event, index);
    };
}

OTHER TIPS

Two other things to note on this subject.

If you must utilize a native Event directly then you should pretty much always make sure and use these last three optional params :

myObject.addEventListener( Event.COMPLETE, myFunction, false, 0, true );

Check Grant Skinner's post on the subject here : http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html

And the very best practice of all is to ALWAYS (seriously always) use Robert Penner's Signals (instead of custom events) and his NativeSignals (to wrap needed native Flash events).

Five times faster than Flash's native events. Always safe with weak references. Any number of typed payload(s) in each Signal.

Get the SWC here : https://github.com/robertpenner/as3-signals

Signals were designed to solve the very problem you are having. Imagine instead of creating an array and managing that to remove all listeners if you could just call :

signalBtnClicked.removeAll();

or

signalBtnClicked.addOnce( function( e : MouseEvent ) : void { /* do stuff */ } );

Knowing that the closure you just created will immediately be dereferenced once it is called and happily go night night when the GC makes its rounds.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top