AS3 Events - 7 things I've learned from the community

September 6, 2009

Since posting my initial critique of AS3 events, I've learned a fair bit through community feedback. Thanks everyone for your comments and corrections; I edited my original post accordingly. Conversations on Twitter brought out further subtleties of how developers work with AS3 events.

I still have more critique in me, but first I'll eat a little humble pie. I haven't actually worked much with custom events in the display list. Most of my AS3 development has been in code libraries that operate independently of the display list. Thus, my custom events generally don't need to bubble or cancel. So when I look at the AS3 event system, I see APIs that often add clutter without a benefit to my project. For developers building RIAs, AS3 event capturing, targeting, bubbling and canceling is wonderful. The standard is called DOM Level 3 Events for a reason. It's great if you're in a DOM, but that doesn't mean it's the most usable solution for events in general. But I'll have to leave that discussion for the next critique.


What I've learned about AS3 events in the last 3 weeks


  1. Custom events can bubble when you dispatch them from a DisplayObject.
    I'd never tried this, and thought that only Flash Player events (MouseEvent, etc.) could bubble.

  2. Custom events can be canceled.
    The Event APIs cancelable, preventDefault() and isDefaultPrevented() are not just for Flash Player events.

  3. Despite Grant Skinner's argument for using weak listeners, some experienced developers choose not to.
    Some say relying on weak references has caused more problems than anticipated. However, Grant advocated always removing listeners explicitly. Weak references are just "an added level of security".

  4. Flash Player 9 didn't always garbage collect weak references.
    This is fixed in Player 10.

  5. Storing method references in a weak keys Dictionaryis buggy.
    References may be duplicated or garbage collected prematurely. Technically, this isn't part of AS3 events. But when trying to extend the event system, you may use a Dictionary to store listeners or callbacks. Developers who've done this have learned not to use weak keys.

  6. When an event has no listeners, EventDispatcher.dispatchEvent() is unnecessarily slow.
    Grant Skinner's patch is a 5x speedup, apparently. 

  7. Some say listener priorities are smelly.
    Like MovieClip.depth in AS2, priority numbers introduce dependencies into your code. These become increasingly difficult to manage in larger systems, as new code must take into account the  priorities in existing code.

    An alternative is to rely on the order the listeners are added. The original dispatcher can add itself as a listener first and thus ensure it has first crack at the event. Unless a different listener misbehaves and steals the spotlight with a higher priority...

    This is an interesting one. I haven't had to deal with this issue and I'm not sure what I think yet. The listener order does start to feel similar to the stacking of movie clips in AS2. Who's going to come out on top? Do we need an equivalent of getNextHighestDepth() for listener priorities [shudder]? I'm reminded of how the Macromedia V2 components would grab the highest possible depth with its own depth manager, rendering getNextHighestDepth() useless. Is there a word for bad nostalgia? How about "nastalgia"?

Labels: ,


11 Comments:

Blogger phillip said...

Worth noting that when attempting to bubble custom events, you want to make sure your event class has an override for clone() where you build a copy the custom properties in your event and return that.

1:26 PM  
Blogger Robert Penner said...

Good point, Phillip. How often do you think EventDispatcher calls clone() when bubbling? Does it create a new instance at each level?

1:34 PM  
Anonymous Keith Peters said...

As mentioned in private, I don't usually worry about making event listeners weak references. Partially due to laziness and not wanting to type out all those excess arguments when only the last is necessary, and partially due to the fact that I pretty much always take care of my listeners and remove them as needed. Code for a while in Objective-C without garbage collection and this kind of thing becomes second nature. I also don't think I've ever used priority.

2:48 PM  
Anonymous xleon said...

Some time ago I spent like 3 hours to figure out why my app event flow wasn´t working. Some event was lost in the way. My app had many events going from one place to another and it was really hard to find where this issue was comming from.
After an insane time looking for everything on my code, I realised the issue was an event listener weak reference. For any reason I don´t know yet, the event was diying before getting its goal.

From that day I never use weak references on similar scenarios. Too hard to find...

3:45 PM  
Anonymous xleon said...

And I had a similar problem using priority, I think the same about that, too hard to find if you have any issue.

3:48 PM  
Anonymous Ben Clinkinbeard said...

clone() is only called if an event is manually re-dispatched by passing it to dispatchEvent() again. So if a parent catches it and then does dispatchEvent( childEvent ), clone() will be used and required. Bubbling alone does not call clone(), but overriding it is good practice.

5:13 PM  
Blogger Robert Penner said...

Thanks Ben, that makes perfect sense.

7:55 PM  
Anonymous Jonathan Kaye said...

I noticed something interesting regarding cloning and dispatching a custom event that I can't explain. I have a class in which I create a custom event instance as a class member to dispatch. While handling a timer event defined for the class, I dispatch that event I created (so the event handler is for a different event). When I didn't create clone properly, I noticed it was dispatching an Event but not a custom event. Of course I fixed clone for my custom event, but I thought it was strange that it was cloning an event when the handler was handling a different event, so I didn't think it was "re-dispatching" the event.

9:28 PM  
Anonymous gropapa said...

yes that is true, when you "re" dispatch an Event manually, the clone method() is called, and since this method does return a new Event(...) you must override it.
What i v been thinking lately is why we usually do such thing:
override public function clone():Event{
return new CustomEvent(...);
}

instead of writing this
override public function clone():Event{
return this;
}

we usually write custom events not for disoplay list but for custom classes like an MVC for example.
I have never been using weak references but it would be great that by default when the handler object is null...that the reference is deleted too! I mean we quickly have a big amount of code just putting listeners on each type we have to listene...and we hav to do the same to remove the listeners!
(Sorry for my bad english though)

12:27 AM  
Blogger phillip said...

One thing I thought I'd confirm. Using weak references on your addEventListeners has no issues if you are sure to removeEventListener() right? Naturally, I end up typing too much--but I figure I intend to do all the necessary clean up (and remove listeners) but if I forget one I'm much better off using weak references.

Maybe the advice against weak references is "don't count on them".

Right?

7:29 PM  
Blogger Robert Penner said...

@phillip,
I agree. You should be cleaning up your listeners. One of the goals of my Signals system reduces the amount of code necessary for clean-up. For example, many events are basically callbacks where you will remove the listener when it fires. My Signal has an API for adding one-time listeners that will be removed automatically on dispatch, so you don't have to.

8:17 PM  

Post a Comment

<< Home

© 2008 Robert Penner| Based on a template by Gecko & Fly.

This page is powered by Blogger. Isn't yours?