Recipe 1.17.
Handling Errors
Problem
You want
to programmatically detect
when certain errors occur and handle them using code.
Solution
Use a throw statement to throw an error
when it is detected. Place any potentially error-generating code
within a try block, and
then have one or more corresponding catch blocks to handle possible
errors.
Description
Flash Player 8.5 supports a
try/catch methodology for handling errors in
ActionScript. That means you can write code that can intelligently
deal with certain error types should they occur. While you cannot
handle syntax errors (the .swf won't even compile in that
case), you can handle most other error types, such as missing or
invalid data. The benefit is that you can attempt to resolve the
situation programmatically.
An example may help to illustrate when and how
you might use try/catch methodology: Consider an
application that draws a rectangle based on user-input dimensions.
To draw a rectangle within the application, you want to have
certain range limitations on the dimensions the user can input. For
example, you may want to make sure the values are defined, valid
numeric values greater than 1 and less than 200. While there are
certainly ways you can work to ensure the quality and validity of
the data before even attempting to draw the rectangle, you can also
use try/catch methodology as a fail-safe. You can
have Flash attempt to draw the rectangle, but if the dimension
values are detected to be invalid or out of range, you can throw an
error that can be handled programmatically. At that point you can
do many things, from simply skipping the action, to substituting
default data, to alerting the user to enter valid data.
There are two basic parts involved in working
with errors in ActionScript: throwing the error and catching the
error. There are several errors, which are thrown automatically by
the player, such as IllegalOperationError, MemoryError, and ScriptTimeoutError. These are in
the flash.errors
package. But you can also detect when an error has occurred and
throw your own custom error. You can throw an error using the
throw statement. The throw statement uses the
throw keyword followed by a value or reference that should
be thrown. Most frequently you should throw an Error object
or an instance of an Error subclass. For example:
throw new Error("A general error occurred.");
As you can see, the Error constructor
accepts one parameter, a message to associate with the error. The
parameter is optional, and depending on how you are handling the
errors, you may or may not choose to use it. However, in most cases
it makes sense to specify an error message. It is possible, then,
to log the error messages for debugging purposes.
Once an error has been thrown, Flash halts the
current process and looks for a catch block to handle the
error. This is where the try and catch blocks come
into play. Any code that could potentially throw an error should be
enclosed in a try block. Then, if an error is thrown, only
the code in the try block is halted, and the associated
catch block is called. The following is the simplest
scenario:
try {
trace("This code is about to throw an error.");
throw new Error("A general error occurred.");
trace("This line won't run");
}
catch (errObject:Error) {
trace("The catch block has been called.");
trace("The message is: " + errObject.message);
}
The preceding code produces the following in the
Output panel:
This code is about to throw an error.
The catch block has been called.
The message is: A general error occurred.
Of course, the preceding example is overly
simplistic, and you wouldn't realistically use code in an actual
application, but it does illustrate the basic process. You can see
that as soon as the error is thrown, the try block is
exited, and the catch block is run and passed a reference to
the Error object that was thrown.
Much more frequently, the error is thrown from
within a function or method. Then Flash looks to see if the
throw statement within the function is contained within a
try block. If so, it calls the associated catch block
as you've seen already. However, if the throw statement in
the function is not within a try block, Flash exits the
function and next looks to see if the function call was made within
a try block. If so, it halts the code in the try
block and runs the associated catch block. Again, a very
simple example:
private function displayMessage(message:String):void {
if(message == undefined) {
throw new Error("No message was defined.");
}
trace(message);
}
try {
trace("This code is about to throw an error.");
displayMessage( );
trace("This line won't run");
}
catch (errObject:Error) {
trace("The catch block has been called.");
trace("The message is: " + errObject.message);
}
In the preceding example the Output panel would
display the following:
This code is about to throw an error.
The catch block has been called.
The message is: No message was defined.
As you can see from the output, the code works
very similarly to the way in which the previous example worked,
except the throw statement is hidden within a function instead of
being called directly within the try block. The advantage is
that you can start to then create functions and methods that are
intelligent enough to know if and when to throw errors. You can
then simply use those functions and methods within try
blocks, and you can handle any errors should they occur.
The following code illustrates a more realistic
example:
// Define a function that draws a rectangle within a specified sprite
private function drawRectangle(sprite:Sprite, newWidth:Number, newHeight:Number):void {
// Check to see if either of the specified dimensions are not
// a number. If so, then thrown an error.
if(isNaN(newWidth) || isNaN(newHeight)) {
throw new Error("Invalid dimensions specified.");
}
// If no error was thrown, then draw the rectangle.
sprite.graphics.lineStyle(1, 0, 1);
sprite.graphics.lineTo(nWidth, 0);
sprite.graphics.lineTo(nWidth, nHeight);
sprite.graphics.lineTo(0, nHeight);
sprite.graphics.lineTo(0, 0);
}
Now we can call the drawRectangle( ) method using a try/catch
statement.
try {
// Attempt to draw two rectangles within the current sprite.
// In this example it is assumed that the variables for the dimensions
// are retreiving values from user input, a database, an XML file,
// or some other datasource.
drawRectangle(this, widthA, heightA);
drawRectangle(this, widthB, heightB);
}
catch(errObject:Error) {
// If an error occurs, clear any rectangles that were drawn from
// the sprite. Then display a message to the user.
this.graphics.clear( );
tOutput.text = "An error occurred: " + errObject.message;
}
In addition to the try and catch
blocks, you can also specify a finally block. The finally
block contains code that is called regardless of whether an error
was thrown. In many cases the finally block may not be
necessary. For example, the following two examples do the same
thing:
//Without using finally:
private function displayMessage(message:String):void {
try {
if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
}
trace("This is the last line displayed.");
}
//With finally:
private function displayMessage(message:String):void {
try {
if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
}
finally {
trace("This is the last line displayed.");
}
}
However, the finally block runs no matter
what occurs within the try and catch blocks,
including a return statement. So the following two functions
are not the equivalent:
//Without using finally:
private function displayMessage(message:String):void {
try {
if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
return;
}
// This line won't run if an error is caught.
trace("This is the last line displayed.");
}
//With finally:
private function displayMessage(message:String):void {
try {
if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
return;
}
finally {
// This runs, even if an error is caught.
trace("This is the last line displayed.");
}
}
You can create much more complex error handling
systems than what is shown in this recipe. Throughout this book you
will find examples of more complex error handling in appropriate contexts.
|