ExternalInterface callback exception handling in ActionScript 3

I had a problem. I needed to make sure exceptions (both manually thrown and unexpected run-time errors) thrown by my Flash/ActionScript 3 application were handled, silent failures were a complete no-no. At the first glance, this seems quite easy.

One way is to enclose your entire code in a try-catch block. It might seem unsightly if you have a lot of code, but becomes quite nice if you use a simple include:

try {
    include "main.as";
}
catch (e:Error) {
    // Handle it.
}

Flash player 10.1 also introduced an event handler for this specific purpose. As far as I can tell, it does the exact same thing as the try-catch block, just in a more OOP fashion. You attach an UncaughtErrorEvent handler to the main movie object (through its loaderInfo property) and any unhandled exceptions will end up there:

loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, ErroHandler);

function ErrorHandler(event:UncaughtErrorEvent):void {
    // Handle it.
}

Which is all good and fine, but here’s the catch: this won’t work on ExternalInterface callbacks. Because the code exectued by an ExternalInterface callback is not run in the context of the main movie clip. This effectively puts it outside the try-catch block (which is defined within the main movie) and the event listener (which is attached to the main movie).

The only workaround I could find was to encase every external callback function in it’s own try-catch block, which then manually triggers the UncaughtErrorEvent of the main movie clip (through a static reference). Since my app has a lot of external calls (it interacts heavily with JavaScript of the page it’s on), I also added a wrapper function to reduce code and improve maintenance & readability. Here’s how it looks:

loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, ErroHandler);

function ErrorHandler(event:UncaughtErrorEvent):void {
    // Handle it.
}

ExternalInterface.addCallback("ChangeAngle", function(...$args) { ExternalCall(SwitchAngle, $args); } );
ExternalInterface.addCallback("AddText", function(...$args) { ExternalCall(NewTextAddon, $args); } );
ExternalInterface.addCallback("AddImage", function(...$args) { ExternalCall(NewImageAddon, $args); } );

function ExternalCall($function:Function, $args:Array=null):* {
    try {
        return $function.apply(null, $args);
    }
    catch (e:Error) {
        GlobalInstances.MainMovie.dispatchEvent(new UncaughtErrorEvent("uncaughtError", true, true, e));
    }
}

Hope this helps someone as stuck as I originally was. Or maybe someone can point out a better way of accomplishing this.

Leave a Reply

Your email address will not be published. Required fields are marked *