AS4 Wish: Shorthand Eventing with Flows
One thing I love and hate about ActionScript 3 is the eventing model. I love the departure from AS2 type callbacks. And I love the way it allows me to have loosely coupled classes. Anyway, to rephrase and refrain from stating the obvious, I think eventing is awesome.
But after the AS3 honeymoon … … I resent the number of times a day I find myself typing out object.addEventListener(…,…) and then object.removeEventListener(…,…). I resent it even more when I know that the event shall only happen once, normally for some kind of COMPLETE event; thus making the first line of my handler always be IEventDispatcher(e.target).removeEventListener(e.type,…). It’s just too verbose, especially when it’s such a core feature of ActionScript.
Now, I’m not trying to crawl onto the recent AS3 is harder and takes longer bandwagon here. I’m ecstatic that AS2 is not longer a daily thing for me; I wouldn’t even go back to it for a pay-rise. But, I feel something could be added to AS3 to make eventing less verbose and less prone to things such as memory leaks.
What makes eventing so verbose? Well, for every event we want to subscribe to, we do:
1) Add Listener
object.addEventListener(”eventtype”, eventHandler);
2) Create a function to handle that event
private function eventHandler(e:Event):void {
trace(”hello world”)
}
3) Remove Listener
object.removeEventListener(”eventtype”, eventHandler);
The creation of a handler function per event group is something that especially rubs me the wrong way, but when working in an asynchronous language, what else can you do? I propose a new concept called a “Flow”. A Flow mimics the blocking nature of synchronous languages while keeping inline with the asynchronous environment of ActionScript.
To outline my proposal, I’m only going to give a commented example, but I hope from it you’ll be able to take away the key concepts. And I stress concepts! As this probably won’t be the best syntactic solution.
class FlickrPhotoGizmo extends EventDispatcher {
// Note that this is a "flow" and not a function.
// Flows seem to be able to "block" until a matching event type is dispatched
// Flows work with existing AS3 code .. ie. imageLoader.addEventListener(Event.COMPLETE, ...)
// A flow will always dispatch a "FlowEvent" of constant type on return.
// Flows are a one time deal; no need to keep listeners around once completed.
// Null returns shall be detailed in the "FlowEvent" for later inspection.
// The "->" notation is our "syntax sugar"
public flow loadAndFilterImage(searchTerm:String):BitmapData {
var imageLoader:FlickrLoader = new FlickrLoader();
var imageFilter:ImageFilter = new ImageFilter()
var resultBitmap:BitmapData;
imageLoader.getMostPopular(searchTerm) -> {
Event.COMPLETE -> resultBitmap = imageLoader.bitmapData;
Event.NO_MATCH -> return null;
}
imageFilter.filter(resultBitmap) -> {
Event.COMPLETE -> {
resultBitmap = imageFilter.filteredBitmapData;
imageFilter.fooClean()
}
Event.ERROR -> return null;
}
imageFilter.filter2(resultBitmap)
-> Event.COMPLETE
-> resultBitmap = imageFilter.filteredBitmapData;
return resultBitmap;
}
}
// wrapper app
funtion init():void {
var gizmo:FlickrPhotoGizmo = new FlickrPhotoGizmo();
// let's load an image with happy people, then filter it...
// ... then give the result to the "imageReady" function
// remember: this is a one time "FlowEvent" event that results from a "flow"
gizmo.loadAndFilterImage("happy people") -> imageReady;
addPermEventHandlers();
}
function imageReady(e:FlowEvent):void {
// no need to call removeEventListener as this is a one time event
var image:BitmapData = e.yield;
if (image) {
addChild(new Bitmap(image))
} else {
var errorEvent:Event = e.nullEvent
switch (errorEvent.type) {
case Event.NO_MATCH: trace("no happy people found :(")
}
}
removePermEventHandlers();
}
// maybe we want this new syntax for permanent listeners too .. note: "o"
// (i know "o" might be a bad pick .. but just go with it)
function addPermEventHandlers {
o-> MouseEvent.UP -> mouseUpHandler
stage o-> KeyboardEvent.UP -> keyPressHandler
}
// and because we have permeant listeners, we need to be able to remove them .. note: "<"
function removePermEventHandlers {
o<- MouseEvent.UP -> mouseUpHandler
stage o<- KeyboardEvent.UP -> keyPressHandler
}
function mouseUpHandler(e:MouseEvent):void {
trace("we're still loading that image for you, just wait .. blame Flickr")
if (false && "anotherExample") {
// this is short hand for .. this o<- MouseEvent.UP -> mouseUpHandler
// we're able to do it because we're aware of the context
o<-e
}
}
function keyPressHandler(e:KeyboardEvent):void {
}




I think this would make the code less readable and more cryptic due to additional (and slightly different) syntax, no matter how compact. Also I think it will be harder to understand and control things like garbage collection.
Anyway, it was an interesting read that raised many new thoughts, thanks.
Yeah I see your point.
What I’d like to see in the next AS complier is the ability to do preprocessing, so you could implement your own macros. So you could create your ‘->’ and ‘AS3 is the only preprocessing macro that I’m aware of and I don’t see Adobe opening up this phase of the complier to the community. Though it would be great if they did.
There is an easy generic line of code you can use in any event handler to unregister it (for these one time events):
event.target.removeEventListener(event.type, arguments.callee);
I’ve got it as a template in FDT so it’s quick and easy to drop in… I do need to use it quite alot though so it might be nice if there was a cleaner alternative…
I stated that method in my write up, Kelvin.
The point is that event handler functions for one time events are too verbose. That one line alone proves my point.
I love to see events just like C#
For example:
myButton.buttonClick = new buttonEventHandler(onButtonAction);
and:
myButton.buttonClick -= new buttonEventHandler(onButtonAction);
Sorry I meant:
myButton.buttonClick = new buttonEventHandler(onButtonAction);
and:
myButton.buttonClick -= new buttonEventHandler(onButtonAction);
good idea, but i agree with Paul who said it makes it less reabable,
As a C programmer we have syntax expressions which are much more… cryptic than this (much more).
From that view point (and the view point of the verbosity of the current event model) I quite like this flow idea. It has a few kinks in it and a few details need to be fleshed out but I think you could really go somewhere with this if you wrote up a more detailed proposal for Adobe.
I agree that they are pretty heavy weight, what I ended up doing is primarily targetted at interactive, display objects but used ADDED and REMOVED from stage to autowire up events listeners based on their instance names.
http://troyworks.com/blog/2008/07/22/flash-fast-prototyping-and-sketching-ui-with-flowcontrol/
I agree we could use some nicer syntax, but we don’t need anything new, we just need the “expression closures” from JavaScript 1.8. Although, since we have method parameter typing we don’t need “function” at all.
as3 / JS 1.7-
takesFunction(function(x) { return 3 * x });
JS1.8:
takesFunction(function(x) 3 * x);
AS4 (I wish):
takesFunction((x) 3 * x);
And of course, if we had real generics and strongly typed function types could be specified, we could do all sorts of scala-ish wonders.
FWIW, here’s a quick hack to make things *slightly* easier than they currently are:
public function once(f : Function) : Function
{
return function(event : Event) : void
{
IEventDispatcher(event.target).removeEventListener(event.type,
arguments.callee,
event.eventPhase == EventPhase.CAPTURING_PHASE);
f(event);
}
}