Copyright © 2004 O'Reilly Media, Inc. All Rights Reserved.
Essential ActionScript 2.0
By Colin Moock
June 2004
ISBN: 0-596-00652-7
More info... .
Available from booksellers or direct from O'Reilly Media, www.oreilly.com.

Cover image
This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org.

Here's one way to write setWidth( ) so that it generates an exception with a throw statement when an invalid width is received:

public function setWidth (w:Number):Void {
if ((isNaN(w) || w == null) || (w <= 0 || w > Number.MAX_VALUE)) {
throw new Error("Invalid width specified.");
}
width = w;
}

When the throw statement executes, program control is immediately transferred to a special section of code that knows how to deal with the problem (we'll discuss this shortly). In official terms, the code that deals with the problem is said to handle the exception.

In our revised setWidth( ) method, when the value of w is illegal, we use throw to halt the method, and we pass a new Error object out of the method to the section of code (not yet shown) that will handle the problem:

throw new Error("Invalid width specified.");

We also supply a description of the problem—"Invalid width specified"—as an argument to the Error constructor. The code block that handles the exception (again, not yet shown) uses the Error instance to diagnose what went wrong. Notice that setWidth( ) no longer returns a Boolean success or failure value. If the method encounters a problem, it uses the throw statement to both quit and signal a failure. Otherwise, it completes normally and we can rest assured that it performed its job successfully.

Now that our setWidth( ) method includes the throw statement (i.e., now that it might generate an exception), we must adjust the way we invoke it. Previously, we would have used the return value of setWidth( ) to determine what to do in the event of a problem:

var b:Box = new Box( );
var someWidth:Number = -10;

// Check setWidth( )'s return value...
if (b.setWidth(someWidth)) {
// ...setWidth( ) returned true, so no problems; proceed as planned.
trace("Width set successfully.");
} else {
// ...setWidth( ) returned false! ERROR! Invalid data. Display a warning.
trace("An error occurred.");
}

But now, when invoking the new exception-based version of our method, we don't bother checking the return value of setWidth( ). Instead, we set up a code branch using the formal try/catch/finally statement instead of an if/else statement. Here's how the new version looks:

var b:Box = new Box( );
var someWidth:Number = -10;

try {
b.setWidth(someWidth);
// If we get this far, no exception occurred; proceed as planned.
trace("Width set successfully.");
} catch (e:Error) {
// ERROR! Invalid data. Display a warning.
trace("An error occurred: " + e.message);
}

Let's study the preceding code in closer detail. The try keyword tells the interpreter that we're about to execute some code that might generate an exception:

try {
// Code here might cause an exception.
}

In this case, the code we're executing is setWidth( ):

try {
b.setWidth(someWidth);
// If we get this far, no exception occurred; proceed as planned.
trace("Width set successfully.");
}

The catch block handles exceptions generated by the try block. That is, the code in the catch block executes if, and only if, code in the try block generates an exception:

} catch (e:Error) {
// ERROR! Invalid data. Display a warning.
trace("An error occurred: " + e.message);
}

When we invoke b.setWidth( ) within the try block, if setWidth( )'s throw statement doesn't execute (i.e., if no error occurs), then the subsequent statements in the try block execute normally and the program skips the catch block entirely. But if setWidth( ) throws an exception, the program immediately skips to and executes the catch block.

Notice, therefore, the typical structure:

  • Code in a try clause invokes a function that might throw an exception.

  • Code in the invoked function throws an exception using the throw statement if an error occurs.

  • Control returns either to the try block (if no error is thrown) or the catch block (if an error is thrown). The catch block deals with any errors that occur in the try block.

When the catch block is executed, it receives the expression of the throw statement as a parameter. In our present example, within the catch block, the parameter e stores the Error instance created by the throw statement in the Box.setWidth( ) method:

// Here's the throw statement, excerpted from setWidth( ).
throw new Error("Invalid width specified.");

We can use that Error instance to help diagnose the problem. The string passed to the Error constructor is available via the message property of the Error instance. In our example catch statement, we simply display the Error instance's message in the Output panel, as follows:

trace("An error occurred: " + e.message);

The Error class's toString( ) method, which is called automatically when an instance is used in a string context, returns the value of the message property. Hence, in a string context, e and e.message are equivalent. For example, the following two statements are synonymous:

trace("An error occurred: " + e.message);
trace("An error occurred: " + e);

We'll use them interchangeably throughout this chapter.

Note that the parameter listed in a catch block should not need to be declared as a variable before being used. However, due to a bug in version 7.0 of the Flash MX 2004 authoring tool, the compiler (incorrectly) generates an error when a catch block parameter is referenced without first being declared. For example, in version 7.0 of Flash MX 2004, the following code:

public function someMethod ( ):Void {
try {
throw new Error("Some error message.");
} catch (e:Error) {
// Respond to the error.
trace("An error occurred: " + e);
}
}

(incorrectly) generates the following error:

There is no property with the name 'e'.

This bug is fixed in the Flash MX 2004 updater, available at: http://macromedia.com/support/flash/downloads.html. (To work around the bug without installing the updater, simply declare the parameter e as a variable.)

Metaphorically, the code that detects a problem throws an exception (passes an Error object) to the catch block, which receives it as a parameter (catches it).

Incidentally, the try block can throw an error directly. For example, in the following code, the catch block is executed when x divided by y is the numeric value NaN (as is the case when both x and y are 0):

var x:Number = 0;
var y:Number = 0;
var e:Error; // Declare e as parameter to avoid compiler bug
try {
if (isNaN(x/y)) {
throw new Error("Quotient is NaN.");
} else {
trace ("Result is " + String(x/y));
}
} catch (e:Error) {
trace("Error: " + e.message);
}

In the preceding example, you might think that attempting to divide by 0 (when y is 0) would cause ActionScript itself to throw a "Division by zero" exception, but no such luck. ActionScript doesn't throw exceptions. It is up to the developer to check for error conditions and invoke throw as desired. Furthermore, in ActionScript, dividing anything other than 0 by 0 yields Infinity (for positive numerators) or -Infinity (for negative numerators).

Whatever the case, it's more common for try blocks to invoke methods that throw exceptions than for a try block to include a throw statement directly. Later, under "Exception Bubbling," we'll learn more about how errors are transferred from methods to enclosing try blocks. For now, you can simply rely on the rules in the following tip.

Tip

Within a try block, if a statement executes, you can safely trust that all preceding statements have executed successfully. If code in a try block (or a method invoked in the try block) throws an error, the remaining statements in the try block are skipped and the statements in the catch block are executed. If no exception is thrown, the try block completes and execution resumes with the statements immediately following the try/catch/finally statement.

To find out what happens if the error is never caught (or, synonymously, never trapped), see "Uncaught Exceptions," later in this chapter.