Custom ErrorEvents
Chances are you’ve been bitten by the ErrorEvent
class at some point while programming AS3. It’s the asynchronous equivalent to throw
an Error
and it happens when, for example, a Loader
‘s load fails. If you write any code that performs an asynchronous task, perhaps more file loading, you too may want a way to inform users of your class that the task has failed. Just like with Event
, it’s nice to be able to add data on to the standard ErrorEvent
class. How does this work? Let’s dig in and find out.
First, an example of how ErrorEvents
are used in the Flash API. Here’s a bit of code to load an image:
var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete); loader.load(new URLRequest("image.jpg")); function onLoadComplete(ev:Event): void { // do something with the image }
Now suppose that image.jpg can’t be loaded, such as when it’s not found on the hard drive or a server returns the dreaded “404” error code. There are a variety of ways the load can fail, but here’s one of the possible messages you’ll see in a debug Flash Player popup window:
Error #2044: Unhandled IOErrorEvent:. text=Error #2036: Load Never Completed.
This is because handling ErrorEvent
events is mandatory. This includes subclasses like Flash’s IOErrorEvent
above. To suppress this crash all you have to do is listen for the event like so:
var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError); loader.load(new URLRequest("image.jpg")); function onLoadComplete(ev:Event): void { // do something with the image } function onLoadError(ev:Event): void { // do something with the error (or not) }
So how about if we want to create our own ErrorEvent
and require our users to listen for it, lest they get a crash? Well, it turns out that all you need to do is subclass ErrorEvent
and dispatch one. Here’s an example of a class that loads up two images:
public class TwoImagesLoadErrorEvent extends ErrorEvent { public static const LOAD_ERROR:String = "loadError"; public var image1:DisplayObject; public var image2:DisplayObject; public function TwoImagesLoadErrorEvent(image1:DisplayObject, image2:DisplayObject) { super(LOAD_ERROR); this.image1 = image1; this.image2 = image2; } } public class TwoImageLoader extends EventDispatcher { private var loader1:Loader; private var loader2:Loader; private var image1:DisplayObject; private var image2:DisplayObject; public function TwoImageLoader(url1:String, url2:String) { loader1 = load(url1, onImage1Loaded); loader2 = load(url2, onImage2Loaded); } private function load(url:String, onSuccess:Function): Loader { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSuccess); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError); loader.load(new URLRequest(url)); return loader; } private function onImage1Loaded(ev:Event): void { image1 = loader1.content; if (image1 && image2) { dispatchEvent(new Event(Event.COMPLETE)); } } private function onImage2Loaded(ev:Event): void { image2 = loader2.content; if (image1 && image2) { dispatchEvent(new Event(Event.COMPLETE)); } } private function onLoadError(ev:Event): void { dispatchEvent(new TwoImagesLoadErrorEvent(image1, image2)); }
To use this class, it’s just as with Flash API classes like Loader
:
var twoImagesLoader:TwoImagesLoader = new TwoImagesLoader("img1.jpg", "img2.jpg"); twoImagesLoader.addEventListener(Event.COMPLETE, onSuccess); twoImagesLoader.addEventListener(TwoImagesLoadErrorEvent.LOAD_ERROR, onError); function onSuccess(ev:Event): void { // do something with the images, like lay them out vertically... addChild(twoImagesLoader.image1); twoImagesLoader.image2.y = twoImagesLoader.image1.height; addChild(twoImagesLoader.image2); } function onError(ev:TwoImagesLoaderErrorEvent): void { trace("couldn't load images. image1=" + ev.image1 + ", image2=" + ev.image2); }
This custom TwoImagesLoaderErrorEvent
contains the two images that were loaded (or not) and lets us salvage the one that was loaded. Since the loads can happen in any order, we don’t know which will load and which will fail. Above we just log out a more informative error message, but more could be done. In the success case the images are laid out vertically. In the error case, a “missing image” placeholder could replace the one that didn’t load.
In summary, a custom ErrorEvent
class can be a useful tool when writing asynchronous AS3 code. You gain conformity with the Flash API which, presumably, all Flash programmers already know. You also gain the ability to make handling your error event mandatory, lest their be a crash. This is much more useful than simply firing an Event
derivative that has “error” in its name but can be ignored, perhaps to the peril of the application.
Spot a bug? Have a suggestion or question? Post a comment!
#1 by AlexG on October 18th, 2012 ·
Great post! Always like to read you.
Sincerely I knew about this feature but I am glad to review it and see your point of view on custom Events.
Unfortunately I didnt have much time to review all your post about GPU 3D renderers but in future will do and hoping to see some simple examples for those who are not familiar with Stage3D yet. And I would like to hear your opinion about engines like Alternative3D.
Thanks!
#2 by jackson on October 18th, 2012 ·
Thanks. :)
If you’re just starting out with
Stage3D
you might want to take a look at my Introduction to AGAL series (one, two, three) as well as Stage3D Pipeline In A Nutshell. Then you can move on to the whole collection ofStage3D
articles.As for engines like Alternativa3D and Away3D, I haven’t looked into them deeply but I’ve heard good things. They probably make a lot of sense if you’re just trying to do some basic 3D for a game or an app. If you want more control, you should probably read the above articles and dive into the nuts and bolts yourself.
#3 by adampasz on October 18th, 2012 ·
I do this a lot:
function myAsyncCall(onSuccess: Function, onError: Function):void;
It still requires a mandatory error handler, and avoids the cumbersome AS3 event mechanism.