« 'googLinks' updated, code sample Data Validation Tip »

December 14, 2003

...need...more...Errors...

Has anyone been doing a lot of work with Flash's exception handling? I really applaud Macromedia for implementing exceptions in ActionScript, but I'm a bit disappointed by the lack of exceptions, so to speak. All we get is "Error"? And why is it named "Error" and not "Exception" or "Throwable"?

Borrowing from Java, check out the Throwable class. There are two subclasses - Error and Exception. When you throw something (via the throw keyword in Java), it's of type Throwable, or a subclass thereof, which makes sense.

An Error is usually fatal to the program... Errors are things like "out of memory" that the program should not try to gracefully recover from. Whenever an Error occurs, the program should simply die.

An Exception, on the other hand, is not necessarily fatal to a program. Exception handling is the act of detecting a problem in the program and gracefully resolving it.

So why did Macromedia define Error as the only "throwable" we get in Flash? Look at all of the subclasses of Exception that are available by default in Java. The reason these subclasses are useful is so you can write code like this (pseudo-code):

try {
    someConverstionFunction(string);
} catch (an IllegalArgumentException) {
   // arguments passed to function not valid
} catch (a NumberFormatException) {
   // string could not be converted to number
} catch (some other Exception) {
   // any other Exceptions caught here
}

Exceptions are caught from specific to general (subclass to superclass), and as a programmer you list the specific cases (subclasses) first. If something happens in "someConverstionFunction" - based on the Exception type we know what happened, and we can deal with the different exceptions differently. Maybe there was an extra argument passed in that shouldn't have been (remember, Flash doesn't enforce argument #'s in user-defined functions, that's why we have the "arguments" array). Maybe the string couldn't be converted to a valid number, or maybe something else happened that we weren't expecting. No matter the case, we can write code to handle each individual exception.

In Flash, since all we get is one class for exceptions, we have to implement this functionality on our own by creating a subclass of Error for everything that we can think of that could go wrong... which is annoying coming from a Java background. I've found that in most cases I can simply re-use the pre-packaged exceptions in Java. Rarely have I had the need to create my own exception type, yet in Flash I have to create them all the time.

So.. here's "IllegalArgumentException.as" to give you an idea how to create your own exceptions. Yes, it's very simplistic.. but very useful.

(continue reading...)

// in IllegalArgumentException.as
class IllegalArgumentException extends Error {
 	public function IllegalArgumentException () {
  		super.constructor.apply(this, arguments);
  	}
}

And here's some example usage:

function onlyAllow1Param(param) {
 	if (arguments.length > 1) {
  		throw new IllegalArgumentException("Only 1 param allowed");
  	}
 	
 	if (param == "cow") {
  		throw new Error("No cows allowed here");
  	}
 
 	// only 1 param and it's valid, this is where we'd actually do something
 	// useful...
}

try {
 	onlyAllow1Param("one", "two");
 
 	// test with the above line, then test by replacing
 	// the above line with: onlyAllow1Param("cow");
 	
 	// then test one more time with: onlyAllow1Param("something");
 	// .. notice the 3 different results in the output window?
 
} catch (iae:IllegalArgumentException) {
 	trace("IllegalArgumentException caught: " + iae);
} catch (e:Error) {
 	trace("Error caught: " + e);
} finally {
 	trace("Finally block executed");
}

One last thing... does it bug anyone else that you can just throw a string as an error? The statement throw "this is an error" is pefectly legit in ActionScript. This is really buggy though...

try {
 	throw "this is an error";
} catch (e:Error) {
 	trace("Error caught.  e instanceof Error = " + (e instanceof Error));
 	trace(typeof(e));
}

We caught an Error of type "Error" but yet e is not an instance of Error, and the type of e is undefined. Yikes. In this case, we shouldn't have caught anything at all -- the error was not of type "Error" and therefore Flash should've just skipped right over the catch block. File that as a bug report.

Removing the strict typing, we can see that typeof(e) is actually "string" but the following code doesn't work either..

try {
 	throw "blah";
} catch (e:String) {
 	trace(e);  // undefined?  huh?
}

The only way to catch something you throw just as a string is to leave off strict typing completely. This is code that will work:

try {
 	throw "this is an error";
} catch (e) {
 	trace("caught: " + e);
}

So... if you plan on using the new exception error handling in ActionScript, I'd recommend always using the "new" keyword when you throw something, and define/catch appropriate subclasses of Error so that you can write better and more readable code...

Sorry for the rant, but I really feel that Macormedia could've done a better job on this...

Comments

  • Just want to add that the 7.0.1 updater fixes some nasty quirks with exception handling:

    http://www.macromedia.com/support/documentation/en/flash/mx2004/releasenotes.html#Update

    See Additional Bugs Fixed -> ActionScript

  • hey darron,

    did you have any problems with nested exception handling? basically, i'm throwing an exception from within a bunch of nested function calls, and although i'm catching the exception at the innermost level, the exception is being propagated up to the outermost levels.

    for example:

    function foo()
    {
    throw new Exception("Something is broken!");
    }

    function bar()
    {
    try
    {
    foo();
    }
    catch (exc:Exception)
    {
    trace("Hooray! Exception caught by bar()!");
    }
    }

    function zap()
    {
    try
    {
    bar();
    }
    catch (exc:Exception)
    {
    trace("Crap! Exception also caught by zap()!");
    }
    }

    i'm seeing:

    Hooray! Exception caught by bar()!
    Crap! Exception also caught by zap()!

    any thoughts?

  • @Jazz --

    Using the same code you posted, I'm not seeing the situation you described:

    function foo() {
    throw new Error("Something is broken!");
    }
    function bar() {
    try {
    foo();
    } catch (exc:Error) {
    trace("Hooray! Exception caught by bar()!");
    }
    }
    function zap() {
    try {
    bar();
    } catch (exc:Error) {
    trace("Crap! Exception also caught by zap()!");
    }
    }
    zap(); // Hooray! Exception caught by bar()!


    Make sure that all of your exceptions are typed right - if you don't catch the right type of exception it will be propagated up the call stack. It's probably something small in your code somewhere.

    Good luck!