Reply to comment

Come back Delegate, all is forgiven

This originally appeared on my blog back in April 2007. I've cleaned it up a bit and put it here.

The Problem

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!");
}

This is the traditional use of 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!");
}

For the second situation, we're using an extended version of 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!");
}

Infinitely useful. What's more, as our version of 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));

will get 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.

The Solution - a new Delegate!

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));
      }
    }
  }
}

Simple, neh? And here's situation 2:
// 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!

And Now... A Warning!

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:

  1. Callback.create() used inline as above creates an anonymous function closure - returning a Function. You need to keep a reference to that function if you ever want to remove the event listener again.
  2. If used inline as above and you use a weak reference for the event listener - something that is good practice - nothing will keep a strong reference to the created Function object and the callback will be destroyed before it's ever called.

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:

  • When using someone else's API. If they define and dispatch their own Event classes, then I can't tack additional context information on to their code without adding potentially complex wrapper layers. Callback is a nice convenience here.
  • When I just need to do something very simple very quickly.

(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.)

Reply

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <i> <b> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockcode> <h1> <h2> <h3> <h4> <h5> <h6>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]".

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.