This originally appeared on my blog back in April 2007. I've cleaned it up a bit and put it here.
When I started to play with AS3, one of the things I jumped up and down about was the fact that we could finally junk the ubiquitous Delegate that appeared scattered throughout all our code - just in there to make sure that functions had the right context.
Imagine my surprise now I'm building something a little more solid in AS3 to realise that I missed Delegate, and actually needed a replacement.
Why? Well, there are actually two major ways that we use Delegate in AS2 code. First, this situation:
// AS2 code public function initApp():Void { var loader:MyLoader= ?; // Some class I've created loader.onComplete=Delegate.create(this,onLoaderComplete); } private function onLoaderComplete():Void { trace("Loading completed!"); }
Delegate, and works fine - in AS3, it's simply replaced by something like this:// AS3 code public function initApp():void { var loader:MyLoader= ?; // Some class I've created loader.addEventListener(Event.COMPLETE,onLoaderComplete); } private function onButtonPressed(event:Event):void { trace("Loading completed!"); }
Delegate (similar to a class called Proxy which has been around for a while) which allows passing of additional arguments - it lets you do this:// AS2 code public function initApp():Void { var loader1:MyLoader= ?; // Some class I've created var loader2:MyLoader= ?; // Some other class I've created loader1.onComplete=Delegate.create(this,onLoaderComplete,1); loader2.onComplete=Delegate.create(this,onLoaderComplete,2); } private function onLoaderComplete(num:Number):Void { trace("Loader "+num+" has completed!"); }
Delegate tacks arguments on to the end of whatever is already being passed to the function, it's very handy for passing on additional context information to callback functions - for example, if you expect a callback function to be called back with, say, the MovieClip that's just been loaded, saying:// AS2 code loadClip(myClip,Delegate.create(this,onMovieClipLoaded,myExtraInfo));
onMovieClipLoaded called back with two parameters, the expected movieclip and myExtraInfo.
Very very handy. But - I can't find it in AS3's new callback syntax. They've removed the need to worry about the context object - situation 1 - but have provided no support for situation 2. As far as I can see.
I find it hard to believe that other people don't need situation 2 from time-to-time. Yes, there are other ways around the issue - such as subclassing the Event class, if you are the person writing the event-generation classes, but if you're using other people's components and need to pass additional information around it can all get a bit tricky.
So here's my answer to Delegate or Proxy for AS3.
// (c) 2007 Ian Thomas // Freely usable in whatever way you like, as long as it's attributed. package com.awen { public class Callback { // Create a wrapper for a callback function. // Tacks the additional args on to any args normally passed to the // callback. public static function create(handler:Function,...args):Function { return function(...innerArgs):void { handler.apply(this,innerArgs.concat(args)); } } } }
// AS3 code public function initApp():void { var loader1:MyLoader= ?; // Some class I've created var loader2:MyLoader= ?; // Some other class I've created loader1.addEventListener(Event.COMPLETE,Callback.create(onLoaderComplete,1)); loader2.addEventListener(Event.COMPLETE,Callback.create(onLoaderComplete,2)); } private function onLoaderComplete(event:Event,num:Number):void { trace("Loader"+num+" has completed!"); }
So there we go. A new Delegate. If anyone can think of an easier solution, let me know!
The above code works, but I don't recommend it - it came from my early experiments with AS3 and migration from AS2. Since really getting into AS3 I've hardly used it. There are two obvious problems:
Both these can be got around by keeping a local reference to the callback; but that then removes the simplicity of the whole thing!
Really, I've hardly ever used this code. There are only a couple of situations where I now find it handy:
(I have had one or two people say that use of the callback method throws away type-checking at compile time, which is true and, yes, it's a Bad Thing. However, exactly the same is true of event listeners; the arguments of your listener function aren't evaluated until the event is dispatched. Oddly no-one mentions that. I don't see that as a valid argument against the use of callbacks; but in most other respects, I'd advocate proper use of Event objects.)