AS3 Signals Getting Stronger
September 13, 2009
A lot has happened in the last four days.
The majority of commenters preferred Github to Google Code so I moved the Signals project there:
http://github.com/robertpenner/as3-signals
I am experimenting with mirroring back to the Google Code SVN. It's going ok so far.
I made about 20 commits to Github this weekend. Some highlights:
Since my last post about connecting EventDispatchers to Signals, I had another idea for integration:
Why not use EventDispatcher for the actual dispatching but wrap it in a Signal facade?
Presenting the NativeSignal class, which lets you have your cake and eat it too.
Get Connected
What Happen? We Git Signal
The majority of commenters preferred Github to Google Code so I moved the Signals project there:
http://github.com/robertpenner/as3-signals
I am experimenting with mirroring back to the Google Code SVN. It's going ok so far.
Committing Early and Often
I made about 20 commits to Github this weekend. Some highlights:
- The package is now org.osflash.signals (previously com.robertpenner).
I wanted it to have more of a community feel. Thanks Aral for the namespace! - Listener priority is now supported in ISignal.add().
- ISignal.dispatch() can now send any number of arguments to listeners.
Zero or ten, it's up to you. - Various classes and APIs were renamed for clarity.
- More unit tests and fixes.
Going Native
Since my last post about connecting EventDispatchers to Signals, I had another idea for integration:
Why not use EventDispatcher for the actual dispatching but wrap it in a Signal facade?
Presenting the NativeSignal class, which lets you have your cake and eat it too.
- Take any EventDispatcher, e.g. Sprite.
- Create a NativeSignal that targets an event of the dispatcher:
// in a subclass: click = new NativeSignal(this, 'click', MouseEvent); // or decorating an instance: click = new NativeSignal(theDispatcher, 'click', MouseEvent);
- Enjoy the Signal APIs and features.
- Dispatch from the NativeSignal or the EventDispatcher. Both use Flash's native dispatchEvent().
If you're hesitant to put your trust in new dispatching code, or you want to keep your EventDispatcher options open, this is the gateway drug for you. You don't have to give up anything. All the native functionality stays, and the ISignal interface can be piped in like frosting, wherever you like. Doesn't that sound delicious?
Get Connected
- I set up a Google Group for AS3 Signals for discussion and updates via email.
- Here's my Twitter. I'm using the #as3signals hash tag.

16 Comments:
I noticed that if the eventObject to be dispatched is not null but evaluates to false in a boolean contex in Signal.as, line 129 and 140, the test fails.
maybe it makes sense to change:
129 if (args.length && eventObject)
140 if (!eventObject)
to:
129 if (args.length && eventObject != null)
140 if (eventObject == null)
Good catch, Anonymous. I'll make a unit test that captures this bug and fix. Unless you want to fork the repo and do it yourself. =)
Hey Robert. I'm glad you decided to go ahead and implement a signals/slots system. I'm really interested to see where it leads. I perused the source code a little bit and so far my favorite feature is addOnce(). I think I'll add that to the event system I'm currently using.
Hi Rob,
If I addOnce then add, the function reference is maintained in the onceListeners dictionary. I'm not sure what the preferred behaviour is, but it feels to me that the 'onceListeners' reference should be removed.
Alec, that's an interesting point. With addOnce() currently, "the first one wins". Your suggestion would be "the last one wins".
I looked at what flash.events.EventDispatcher does with listener priority, which has the same issue. They chose "first one wins". The documentation specifically says that if you want to change the listener priority, you have to remove the listener and then add it with the new priority. I imagine this is to make it more difficult to accidentally change the priority.
I am inclined to follow the priority pattern for addOnce() so they behave the same way.
Hi Jackson, thanks for pointing me to Qt signals/slots. It was a source of inspiration and provided a catchy name. =)
I think my instincts are that add trumps addOnce; and even maybe that addOnce throws an error if add is already defined (though that makes things messier internally).
addOnce is weaker than add and is a special case. That said, first call wins is easier to declare as a principle so you're probably right.
Hi Robert , AlecMce just explained me his thoughts (he works 45 cm away from my desk :) ) about add and addOnce and I agree with him.
When you specifically add a signal using only add it means that you are strongly managing it so your code is expecting to handle that signal every time it's dispatched.
If then you try and addOnce the same listener you should get an error.
Now the question could be: what if I REALLY want to add that listener once and so override the previous add functionality?
You can either try catch the addOnce or if you know about it just remove the listener before adding it once.
On the other case if I addOnce first and then add , the last operation will override the first silently.
I would expect that add has always the priority on addOnce.
hope I explained my thoughts well :)
Hi Simone,
Thanks for the explanation. I understand it, but I don't see a big win in exchange for the added logic and documentation necessary. I would rather keep the behavior simple and consistent with priority, so other users don't have to deal with the subtleties.
Fair enough :)
I think the big win would be saving debugging time for devs and avoiding nasty bugs in the long term.
Btw good job it was time to evolve the Flash event system :)
I may be missing something, but non-display classes still can't bubble events, right? Not unless they have a 'parent' and the parent implements IBubbleEventHandler?
I'm getting around this in my Flex application by simply having an implements attribute on my main mxml file:
<s:Application ...
implements="org.osflash.signals.IBubbleEventHandler" />
and then in my non-display class:
public function get parent():Application {
return Application(FlexGlobals.topLevelApplication);
}
This way, I can have a degree of global event handling. This could be useful for something like: printing error messages to a statusbar without having to have that statusbar explicitly listening for every error.
If there's a better way of doing this, let me know. It'd be good to see some more code examples.
...oh and of course your topLevelApplication (eg Main.mxml) must actually implement IBubbleEventHandler:
public function onEventBubbled(event:IEvent):void {
//Handle Event Here
}
@secoif, that looks right.
Hey...
.. I used addOnce on NativeRelaySignal, but it wouldnt fire. Looking into your code i saw, that you decided to override add/remove but not addOnce/removeOne in the NativeRelaySignal class. Is there a particular reason for this?
And why do you have NativeSignal not implement ISignal.. Makes expensive to switch between NativeRelaySignal and NativeSignal. Reason for this?
@kzm,
Excellent points.
I didn't override addOnce() because until recently, Signal.addOnce() called add(). But I should have had a test for NativeRelaySignal.addOnce(), which would have broken. Feel free to fork on Github and write the test and/or fix. Otherwise I'll get to it in the next couple of days.
The reason for the different interfaces is that IEventDispatcher.dispatchEvent(event:Event) is much more rigid than dispatch(eventObject:Object = null, ...args).
I could have tried to have NativeSignal implement dispatch() with extra args which would be ignored, but it seems like a lie--it implies you can use it in a way that is not implemented. And it would allow more incorrect code through the compiler.
Also, with NativeSignal, target is always an IEventDispatcher. If I left it as Object, it would require casting.
But all signals implement IListeners. The difference is only in IDispatcher vs INativeDispatcher. You can expose your signals as IListeners in your public APIs. Then to dispatch() you cast them to INativeDispatcher or IDispatcher internally. That allows you to prevent anyone else from calling dispatch() from outside your class.
Thanks for the code and signals look interesting but I wish it was on google svn which I find very easy to use rather than github which I do not.
Post a Comment
<< Home