Reflecting on as3signals
Robert Penner has recently produced a new AS3 library called as3signals (which can be found on both Git Hub and Google Code. I have used it in one small ongoing project, and I am really impressed with it.
as3signals includes two methods for adding listeners: an add listener which permanently adds a listener until a remove listener is called, and an addOnce listener which is a one-time listener only. This is a really welcome addition to the event model. Current projects are littered with code like this:
objectThatDispatchesEvent.addEventListener(EventType, onEvent, [false, 0, true]);
private function onEvent(event:Event):void
{
objectThatDispatchesEvent.removeEventListener(EventType, onEvent);
...actual code for handling event...
}
Penner’s signals performs the task much more neatly:
signal.addOnce(onEvent);
private function onEvent(data:Data):void
{
...actual code for handling event...
}
An interesting problem arises by having two methods for adding listeners that have different behaviours. What happens when you call first one, then the other, without calling a remove method in between? There are three main ways in which you could imagine the framework responding to this situation.
- Overwrite Model Each time a call redefines the relationship between signal and listener. In this model, subsequent calls overwrite the relationship;
- First-Wins Model The first call defines the relationship between signal and listener. In this model, subsequent calls are ignored. To change the relationship the current relationship should be first removed;
- Throw Error Model If you attempt to change the relationship between signal and listener without first calling
removethen an error is thrown.
Overwrite Model
The add method defines a stronger relationship between signal and listener than the addOnce method. If I had previously called signal.addOnce(listener) and then called signal.add(listener), I am intentionally strengthening the signal-listener relationship, and want the relationship to change. Conversely, I would expect the relationship to weaken.
A weakness of this model is that if you were to inadvertently change the relationship between the signal and listener, this would be extremely difficult to debug.
First-Wins Model
If you assign an event listener with addEventListener in the as3 native event model then you assign a priority to the listener which defines the order in which listeners are called (an optional parameter, which is 0 by default). In the as3 native event model, you cannot change the listener priority with further addEventListener calls unless you first call removeEventListener. Subsequent listener priorities are ignored. Similarly then, we could take this approach for add and addOnce. All subsequent calls are ineffective unless remove is called.
A weakness of this model is that if you intentionally change the relationship between the signal and listener, then your change should be effective! The following code certainly looks like it should work, but in fact the second line does nothing at all!
signal.addOnce(listener); signal.add(listener);
Throw Error Model
There are scenarios in which developers might purposefully use both add and addOnce methods without calling a remove method in between. However, there will likely be cases where the two methods are called interchangeably by mistake. This may not be easy to debug, and we could easily help the developer out here by changing the functionality so that if signal.addOnce(listener) and signal.add(listener) are called in either order without an intervening signal.remove(listener), that an error is thrown.
In many ways, this is an extension of the ‘first-wins model’, because it preserves the functionality of that model. However, it recognises that the developer that intends to change behaviour might write something like:
signal.addOnce(listener); signal.add(listener);
and handles it by throwing an error. The developer can then quickly change his code to either simply
signal.addOnce(listener);
or
signal.addOnce(listener); signal.remove(listener); signal.add(listener);
Summary
Penner argues that since the as3 native event model follows the ‘first-wins model’ with respect to priorities, this is the preferred model for handling addOnce and add calls. This argument is a little unsatisfactory, not least because if native as3 design decisions were in-themselves satisfactory, he wouldn’t have written as3signals in the first place!
My immediate preference was for the ‘overwrite model’, but I can see why people might adopt the ‘first-wins model’. I imagine that there is a relatively even split between the two camps. Therefore, I believe that throwing an error is the least confusing of all possible decisions. If an error is thrown, it is more difficult to write code that has unintended consequences. It forces the inclusion of remove methods, which makes code more readable. It is easy to explain and to document.
Hopefully it is obvious that I have enormous respect Robert Penner and the as3signals project. However, these small design decisions can have a large influence on workflow and they are important to get right. Penner has given the code an osflash package and he is clearly committed to writing this for the community. I first argued this point very briefly on one of his blog posts on as3signals, but it is an issue that deserved more serious attention. This sort of design decision would be well served by community discussion. If you have an opinion, please comment!
General Notes on Events
AS3 signals came out of a discussion on the problems that exist in the current event model. It is difficult to see Adobe deprecate this model any time soon, because their component framework, Cairngorm and the greater Flash/Flex community are now so dependent upon this model that it is unfeasible to imagine that they would abandon it.
The only way that the signals approach could be adopted en masse is probably if Adobe migrate to it in some future language ASx {x > 3}, or if some major libraries start to adopt it as their event model of choice.
In an earlier post of mine AS4 Thought Experiment, I attempted to think through my preferred API for a future language’s event model. The article was a response to community chatter about the event model, in part driven by Penner.
We share similar perspectives on how an event model ought to work. On reflection, I prefer Penner’s implementation with event.add(listener) and event.addOnce(listener) to my proposed event += listener, though I still stand by the argument that ideally, events should be demarcated from elements of an object through their own syntax; in my example by using #event... and by defining them in interface or class through public event drag or public event #drag. This, of course, is never going to happen!.
-
http://twitter.com/alecmce alecmce
-
http://twitter.com/robpenner robpenner
-
http://twitter.com/DesignRT DesignRT
-
http://twitter.com/wikiup wikiup
-
http://www.jacksondunstan.com/ Jackson Dunstan
-
http://www.jacksondunstan.com Jackson Dunstan
-
http://alecmce.com Anonymous
-
http://alecmce.com alec
-
http://www.jacksondunstan.com/ Jackson Dunstan
-
http://www.jacksondunstan.com Jackson Dunstan
-
http://twitter.com/FGL_Joe FGL_Joe
-
http://twitter.com/retrogamer4ever retrogamer4ever
-
http://alecmce.com Anonymous
-
http://alecmce.com alec
-
http://www.mikestead.me/ Mike Stead
-
http://www.mikestead.me Mike Stead
-
http://alecmce.com Anonymous
-
http://alecmce.com alec
-
http://robertpenner.com/ Robert Penner
-
http://robertpenner.com Robert Penner
-
http://alecmce.com Anonymous
-
http://alecmce.com alec
-
http://twitter.com/alecmce alecmce
-
http://robertpenner.com/ Robert Penner
-
http://robertpenner.com Robert Penner
-
http://www.RivelloMultimediaConsulting.com/ Samuel Asher Rivello


