September 6, 2009

My Critique of AS3 Events - Part 2 - The Dispatchening

Come with me as I chase more windmills. could be easier to clean up

removeAllEventListeners(), where are you? Everyone's been looking for you.
And wouldn't it be great if I could mark some listeners to be removed automatically the first time called?

Whatever, this is OOP, it shouldn't be too hard to subclass and do it myself.
Let's create a new class, override addEventListener() and... is difficult to extend

Oh snap, looks like my subclass can't access all those listeners in EventDispatcher.
Guess I'll maintain my own array of listeners so I can remove them all easily. It's been done before:

But now my array has strong references to all the listeners. The useWeakReference option won't help me now.
Ohhh, I know!
can haz weak references?
Theoretically, yes.
Unfortunately, though, storing methods in a weak keys Dictionary is buggy.

Besides, Dictionary has no concept of order.
What if I need to know what sequence the listeners are in?
Would I create a weak reference Dictionary for each listener and have an array of dictionaries?
It's been done, but I'm not going to consider that until the Dictionary bug is fixed.
Hmm... Can haz special array with weak references? Keep dreaming.

The point being, is a black box--the frustrating kind. Adobe Flash Player engineers, if you're reading this and you know a magic namespace to open up the listeners, please let me know. While we're dreaming, how about a look at the source code for EventDispatcher?

Oh well, at least we have the IEventDispatcher interface. It seems like a lot of work, but I should be able to write my own dispatcher from scratch and satisfy the interface, right?

IEventDispatcher cannot be implemented

Not entirely true. It can, but not with pure AS3. But I don't know that yet.

So I work away building my own implementation of IEventDispatcher. I'm adding listeners, I'm removing listeners, I'm even removing all listeners! Things are looking up.

I'm at the exciting part now: the dispatchEvent() method. Ok, find the listeners array for the event type, good, good. Now I'll just set the property to this and iterate through the--

GACK! is read-only


I can picture the meeting in the secret DOM Level 3 Dungeon:

Level 3 Paladin:  "Would anyone ever need to change the event target?"
Level 3 Seer"Inconceivable!"

But, there must be some way of changing the event target, right?

IEventDispatcher cannot be implemented without using EventDispatcher

You see, has a secret alliance with
Only EventDispatcher is entrusted with the awesome power to change target.
We're so much safer that way.
And yes, you will have to extend because there is no event interface.

Here's an offer you can't refuse: implement an interface using one special implementation of that interface!

You can write your own dispatcher if you like, but if you have a hang-up about target and currentTarget being null all the time, you'll have to instantiate EventDispatcher and have it dispatch the event for you. Which means it will need all your listeners and your Gmail password. Which makes you wonder why you even bothered.

I hope this gives a sense of the obstacles that arise when trying to extend the AS3 event system. It fights you at every turn. Please let me know if I've overlooked or misunderstood anything.

At least this article was fun to write. It solidifies my rationale for building and using my own event system when I don't need to integrate with the display list.


Ben Clinkinbeard said...

Would an array of listener UIDs work?

Robert Penner said...

Ben, do you mean in order to have weak references in an array? Can you expand on your idea?

professor torpedo said...

I am sitting here looking at the exact same problem. i'll sum up my observations in a few minutes....

Ben Clinkinbeard said...

Yea, that is what I meant but now I realize that only solves half the problem. It would allow you to store an ordered list of listeners without creating references but you'd have no way of looking them back up since there is no getByUID() method. I think you're right, we're screwed. :)

professor torpedo said...

I have had tons of success, using the Swiss Flash User's Group ( SFUG ) [ LOVE YOU CATS ! ] events for AS2,
to emulate AS3.

You have the complete source, these are real classes, so when you extend them, you know *exactly* what you're doing. If you want to make some sort of variation and write to the interface, you know exactly what you're changing from the original implementation.

I'm going to try and port to AS3....
we had already modified the sfug class for mixin with an initialize(p_target)... lifted straight outta AS1.

Being able to see all the moving parts really is important....
I once thought EventDispatcher was a black box, coming up from AS1, but in AS2 you really can scour out all the classes. In FDT, with MTASC classes, you can shift click all day around the real guts of your flash app.
MTASC was great for clarifying all of that.
AS3? swc. meh.
AS3 EventDispatcher, great for what it's great for. But if you reaaaaallyyy want to own the process...
in AS2, running the ch.sfug.EventDispatcher was a breeze.

I'll let you know how it turns out. or you can let us know what you think about bringing these up to AS3...

I have also had a lot of luck porting people's various Iterators between AS2 and AS3. again,
if they weren't transparent, you would be seriously crippled....

the sfug classes let us run AS2 side by side with AS3 with super easy ports because of the event model.
Maybe the AS2 eventdispatcher class can save us from the can't touch this AS3 EventDispatcher. maybe.
can't weak references really just be locally scoped variables in the dispatcher ? I might be on crack. but...

thanks again for all your edgy thinking sir. excuse me if i have missed something obvious [again]


Robert Penner said...

Hello Professor Torpedo (how often do I get to say that?),

The SFUG EventDispatcher is almost identical to the AS2 EventDispatcher a work colleague and I developed.

We had the same method signature for adding and removing listeners. The extra for scope allowed us to eliminate Delegate creations all over the place.

I have something fairly different in mind these days--an event system inspired by C#.

Xavi Beumala said...

Hi Robert,

very nice post! If just blogged about a way you could use (with debugger player) to retrieve a list of listeners from an EventDispatcher:

Bart said...

What's also great (NOT) is that you cannot re-use custom events.. (eg: you cannot use an object pool).. so the amount of object creation in a more complex application is simply insane. We tried to, but the blackbox is doing some sneaky stuff that breaks your Event-objects.

EECOLOR said...

In what cases do you want to remove all event listeners?

In quite some years I never found myself in a situation where I needed to do that.

In almost every case it is enough to just remove the event dispatcher object. Can you give a practical example where you need to remove all?

Jamie Lemon said...

In the case where you have got a lot of objects setting up things like maybe Event.ENTER_FRAME and you have that triggering ( in hyperspace sometimes... ) A complex RIA can get like this after a while, believe me .... ( so when you unload a view, remove a display object , start a refresh removeAllEventListeners() would be pretty damn useful ). I've come to Robert's conclusion - that, yes, a central bespoke event listener manager is the way to go...

Anonymous said...

Maybe i'm dumb but the only think i really agree is the removeAllListeners(). All your critics (which are relevants) are made because you want use a different event model (which is not a bad idea at all).

Anyway, wouldn't it possible to use a dictionary with weak references, store the target as the key and an int as the value ? With some work you would have a kind of "reversed array" you could loop through and removeListeners...

My last question would be : how would you handle the flash event model with yours :

Would you have to use both in the same code, depending on what event you wanna use (because some events might be replaced) or maybe internally handle flash event model in your framework ?

Robert Penner said...


Anyway, wouldn't it possible to use a dictionary with weak references, store the target as the key and an int as the value ? With some work you would have a kind of "reversed array" you could loop through and removeListeners...

How can I store the target as the key, when addEventListener() doesn't have a parameter for the target? I don't follow your reversed array idea...

dynamike said...

Hi Robert, I created an as3 util class for removeAllListeners() and more stuff.


ScriptTutor said...

You are funny!.. Anyways you right. However, My personal belief is that IEventDispatcher is just wrong choice of naming. It should've been IEventListener instead. Then there is no need for the confusion it causes. :D

millerthegorilla said...

I'm about to extend Senocular's class to use for a gallery. I have altered the class to extend Sprite, where the original class extends MovieClip. I may change that but if I can't get it working with inheritance then I will try composition. If not then I will return to my own custom listener stack. Check out the following:

weltraumpirat said...

Maybe it's just me, but why don't you override the getter for target and currentTarget and have them return a local variable? I've tried, and you can even implement a setter, so nothing should keep you from creating your own, fully customized IEventDispatcher...

Robert Penner said...

@weltraumpirat, that's a clever idea. But I've long since moved on from EventDispatcher.

james Trickey said...

@weltraumpirat Damn! You beat me to it. I was just about to post the exact same idea.

Just getting into signals now. They seem very interesting! Good work Robert.

I do still have a soft spot for events though. I must say though that week referencing just promotes lazy coding in my opinion. Remove all on the other hand... yummy!


Anonymous said...

Necro-post, but I think it's useful for anyone who chances upon this.

Bart said... cannot re-use custom events.. (eg: you cannot use an object pool).. so the amount of object creation in a more complex application is simply insane...

September 9, 2009 at 3:34 AM

If you're talking about what I think you are, it's actually incredibly easy to re-use (or object pool) objects with a custom event that extends the Event class.

Let's say your custom event class is called "CustomEvent"

When an event is dispatched, it's cloned automatically, and that clone becomes the one stored in your object. So, if you have a CustomEvent stored in an object, it is probably being replaced with an object of the default Event type when clone() is called by the dispatcher. Thus, after one use, the CustomEvent is being replaced by an Event.

The fix: Your CustomEvent class simply needs to override the "clone()" function and call it's own constructor, so it returns a new CustomEvent rather than a new Event. That way, when the dispatcher calls "clone()", a CustomEvent will be generated and stored.