This chapter describes how JavaScript’s exception handling works. It begins with a general explanation of what exception handling is.

What Is Exception Handling?

In exception handling, you often group statements that are tightly coupled.

Let’s look at code without exception handling:

What is the best way to react to an error in at (2)? Clearly, the statement (1) should not be executed anymore. But we wouldn’t want to abort extractAllEntries(), either. Instead, it is enough to skip the current file and continue with the next one. To do that, we add exception handling to the previous code:

  1. function extractAllEntries(fileNames) {
  2. var allEntries = new Entries();
  3. fileNames.forEach(function (fileName) {
  4. try {
  5. var entry = extractOneEntry(fileName);
  6. allEntries.add(entry);
  7. } catch (exception) { // (2)
  8. errorLog.log('Error in '+fileName, exception);
  9. }
  10. });
  11. }
  12. function extractOneEntry(fileName) {
  13. var file = openFile(fileName);
  14. ...
  15. }
  16. function openFile(fileName) {
  17. if (!exists(fileName)) {
  18. throw new Error(+fileName); // (1)
  19. }
  20. ...
  21. }

There are two aspects to exception handling:

  • If there is a problem that can’t be handled meaningfully where it occurs, throw an exception.
  • Find a place where errors can be handled: catch exceptions.

At (1), the following constructs are active:

  1. processFile()
  2. extractAllEntries(...)
  3. fileNames.forEach(...)
  4. function (fileName) { ... }
  5. try { ... } catch (exception) { ... }
  6. extractOneEntry(...)
  7. openFile(...)

The throw statement at (1) walks up that tree and leaves all constructs until it encounters an active try statement. It then invokes that statement’s catch block and passes it the exception value.

Exception handling in JavaScript works like in most programming languages: a try statement groups statements and lets you intercept exceptions in those statements.

  1. throw «value»;

Any JavaScript value can be thrown. For simplicity’s sake, many JavaScript programs just throw strings:

  1. // Don't do this
  2. if (somethingBadHappened) {
  3. throw 'Something bad happened';
  4. }

Don’t do this. JavaScript has special constructorsError Constructors). Use those or subclass them (see ). Their advantage is that JavaScript automatically adds a stack trace (on most engines) and that they have room for additional context-specific properties. The simplest solution is to use the built-in constructor Error():

  1. throw new Error('Something bad happened');
  2. }

The syntax of try-catch-finally looks as follows. try is mandatory, and at least one of catch and finally must be there, too:

  1. try {
  2. «try_statements»
  3. }
  4. catch («exceptionVar») {
  5. «catch_statements»
  6. }
  7. finally {
  8. «finally_statements»
  9. }

Here’s how it works:

  • catch catches any exception that is thrown in try_statements, whether directly or in functions they invoke. Tip: If you want to distinguish between different kinds of exceptions, you can use the constructor property to switch over the exceptions’ constructors (see ).
  • finally is always executed, no matter what happens in try_statements (or in functions they invoke). Use it for clean-up operations that should always be performed, no matter what happens in try_statements:

If one of the try_statements is a return, then the finally block is executed afterward (immediately before leaving the function or method; see the examples that follow).

Any value can be thrown:

  1. function throwIt(exception) {
  2. try {
  3. throw exception
  4. } catch (e) {
  5. console.log('Caught: '+e);
  6. }
  7. }

Here is the interaction:

  1. > throwIt(3);
  2. Caught: 3
  3. > throwIt('hello');
  4. Caught: hello
  5. > throwIt(new Error('An error happened'));
  6. Caught: Error: An error happened

finally is always executed:

  1. function throwsError() {
  2. throw new Error('Sorry...');
  3. }
  4. function cleansUp() {
  5. try {
  6. throwsError();
  7. } finally {
  8. console.log('Performing clean-up');
  9. }
  10. }
  1. > cleansUp();
  2. Performing clean-up
  3. Error: Sorry...

finally is executed after

  1. function idLog(x) {
  2. try {
  3. console.log(x);
  4. return 'result';
  5. console.log("FINALLY");
  6. }
  7. }

Here is the interaction:

  1. > idLog('arg')
  2. arg
  3. FINALLY
  4. 'result'

The return value is queued before executing finally:

By the time statement (1) is executed, the value of count has already been queued for returning:

  1. > countUp()
  2. 0
  3. > count
  4. 1

Error Constructors

ECMAScript standardizes the following error constructors. The descriptions are quoted from the ECMAScript 5 specification:

  • Error is a generic constructor for errors. All other error constructors mentioned here are subconstructors.
  • EvalError “is not currently used within this specification. This object remains for compatibility with previous editions of this specification.”
  • RangeError “indicates a numeric value has exceeded the allowable range.” For example:
  1. > new Array(-1)
  2. RangeError: Invalid array length
  • ReferenceError “indicates that an invalid reference value has been detected.” Usually, this is an unknown variable. For example:
  1. > unknownVariable
  2. ReferenceError: unknownVariable is not defined
  • SyntaxError “indicates that a parsing error has occurred” either while parsing normal code or while parsing the argument of eval(). For example:
  1. > 3..1
  2. SyntaxError: Unexpected number '.1'. Parse error.
  3. > eval('5 +')
  4. SyntaxError: Unexpected end of script
  • TypeError “indicates the actual type of an operand is different than the expected type.” For example:
  1. > undefined.foo
  2. TypeError: Cannot read property 'foo' of undefined
  • URIError “indicates that one of the global URI handling functions was used in a way that is incompatible with its definition.” For example:
  1. > decodeURI('%2')
  2. URIError: URI malformed

Here are the properties of

  • message
  • The error message.
  • name
  • The name of the error.
  • stack
  • A stack trace. This is nonstandard, but is available on many platforms—for example, Chrome, Node.js, and Firefox.

The usual sources of

  • Data: What values do variables have?
  • Execution: In what line did the exception happen, and what function calls were active?

You can put some of the first item (data) into either the message or the properties of an exception object. The second item (execution) is supported on many JavaScript engines via stack traces, snapshots of the call stack when the exception objects were created. The following example prints a stack trace:

  1. > catchIt()
  2. Error
  3. at throwIt (~/examples/throwcatch.js:9:11)
  4. at catchIt (~/examples/throwcatch.js:3:9)
  5. at repl:1:5

Implementing Your Own Error Constructor

If you wantChapter 28 to learn how to do it.