
- A Runtime Error
Perhaps you’re familiar with this image. How embarassing to launch an app only to find that it’s throwing up large, intrusive error windows!
What’s to be done? Well, you have two options:
-
Write perfect code.
-
Use Runtime Error Handling
Some day I’ll elaborate on the secrets of the first option(1, 2), but for the time being I’d like to talk about Runtime Error Correction and Handling.
Let me begin by explaining what a runtime error is. As compared to a Compile Error, which is caught on compilation, or a Logical Error, which is due to fuzzy logic on the part of the programmer, a Runtime Error is an error that occurrs in your code while it is executing.
Flash has several types of Runtime errors that are thrown at different times depending on the type of error that occurrs. As somebody who works with Flash every day, I can say with some confidence that the most important and common ones for you to worry about are TypeErrors, ReferenceErrors, and RangeErrors. You may also find yourself occasionally encountering SecurityErrors and SyntaxErrors, but for now we’ll just worry about the first three.
How do these errors occur? Let’s break it down one by one.
TypeErrors
Type errors generally occur when you improperly coerce the value of one variable to the type of some incompatible “other” variable. What does this mean? Here’s an example:
var mov = getChildByName("myMovieClip");
mov.gotoAndPlay(2);
The code above will throw an error because you’re implicitly (rather than explicitly) trying to coerce a DisplayObject into behaving like a MovieClip. What the hell am I talking about? Well, if you check the documentation for the “getChildByName” method on the MovieClip class, you’ll see that it returns a DisplayObject. What this means is that whenever you call this method without explicitly casting its return object, Flash is going to assume you’re just talking about a vanilla, plain-jane DisplayObject.
Here are two solutions:
var mov = getChildByName("myMovieClip") as MovieClip;
…or…
var mov = MovieClip(getChildByName("myMovieClip"));
Both lines of code do the same thing. For the sake of readability, I prefer the first method.
ReferenceErrors
Reference Errors occur when you try to access or set a property that doesn’t exist on a non-dynamic class. For people who are new to object oriented programming in AS3, this will happen for a while until you really appreciate the difference between a dynamic and a non-dynamic class. Let’s look at an example.
package {
public class Foo() {
public function Foo() {
trace(this.bar);
}
}
}
The example above will throw a Reference Error because the class Foo doesn’t have a property named “bar.” I have two solutions.
package {
public class Foo() {
public var bar:*;
public function Foo() {
trace(this.bar);
}
}
}
In the above example, we just made sure that the public class Foo does have a property named “bar.”
package {
public dynamic class Foo() {
public function Foo() {
trace(this.bar);
}
}
}
In this second example, we made the public class Foo into a dynamic class. What this means is that we can refer to any property we want on Foo, and if it doesn’t exist Flash will create it for us automatically. This may sound like a very tempting proposition, but the first approach is most definitely preferred on this one, which is a point I will explain at a later date. For the time being, just take my word for it: Avoid making your classes dynamic if you can.
RangeErrors
A Range Error occurs when you attempt to refer to an index that doesn’t exist in an Array. For example:
var shitty_bands:Array = [ "Slipknot", "Linkin Park", "Insane Clown Posse"];
trace(shitty_bands[3]);
The code above will generate a range error because the shitty_bands array doesn’t have any entries at an index of 3. You’ve literally referred to an entry that is out of the range of the array, making this a pretty easy error to remember. To solve, you could…
var shitty_bands:Array = [ "Slipknot", "Linkin Park", "Insane Clown Posse"];
trace(shitty_bands[shitty_bands.length-1]);
…use properties of the array to make sure that you don’t refer to entries outside of its range, which also saves you the trouble of actually needing to know the number of entries in the array at any given point (assuming you were just trying to grab the last item in the array), or you could…
var shitty_bands:Array = [ "Slipknot", "Linkin Park", "Insane Clown Posse", "The Black Eyed Peas"];
trace(shitty_bands[3]);
…just add another shitty band to the array.
Admittedly, these are stop-gap solutions that all depend on what you’re actually trying to accomplish with your program. There is one final solution.
Explicit Error Handling
What do I mean by Explicit Error Handling? I mean that you anticipate when portions of your code may throw errors, and set up appropriate control structures to deal with them when they happen. There are two ways to do this, and the method you choose may vary depending on the type of error you anticipate and the amount of code you’re trying to control for. Let’s consider the first method.
Error Event Listeners
Many errors can be taken care of with simple event handlers. Security Errors and IOErrors, for example, have their own event classes that you can listen for.
package {
import flash.net.*;
import flash.events.*;
public class Foo() {
public function Foo() {
var loader:URLLoader = new URLLoader();
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _securityErrorHandle, false, 0, true);
loader.addEventListener(IOErrorEvent.IO_ERROR, _ioErrorHandle, false, 0, true);
loader.load(new URLRequest("data.xml"));
}
}
private function _securityErrorHandle(e:SecurityErrorEvent):void {
trace("SecurityError :: ",e.errorID);
}
private function _ioErrorHandle(e:IOErrorEvent):void {
trace("IOError ::",e.errorID);
}
}
Depending on how large or complex your application is this may be a good solution for you. I know that in some respects I find this easiest to understand as it runs on the existing Event metaphor that I’m accustomed to using for everything else in AS3.
The second method employs a construct which with, if you have done any programming in the past, you are probably familiar.
package {
public class Foo() {
public function Foo() {
try {
trace(this.bar);
} catch (e:ReferenceError {
trace("Reference Error!");
} finally {
// do something else
}
}
}
}
The Try…Catch…Finally construct exists in pretty much every programming language known to man. It’s also pretty simple to understand. The application “attempts” to do whatever is within the “try” statement. If an error occurs, it’s handled in the “catch” statement, and whatever was going to take place in the “try” statement just doesn’t happen. After these steps, the application does whatever is indicated in the “finally” statement. It doesn’t get much easier than that. This is also nice because it forces you to deal with errors at the site of their creation, and to be more thoughtful about where and why errors happen.