This chapter covers how JavaScript handles exceptions.

    As an aside: JavaScript didn’t support exceptions until ES3. That explains why they are used sparingly by the language and its standard library.

    Consider the following code. It reads profiles stored in files into an Array with instances of class :

    Let’s examine what happens in line B: An error occurred, but the best place to handle the problem is not the current location, it’s line A. There, we can skip the current file and move on to the next one.

    Therefore:

    • In line B, we use a throw statement to indicate that there was a problem.
    • In line A, we use a try-catch statement to handle the problem.
      When we throw, the following constructs are active:
    1. readProfiles(···)
    2. for (const filePath of filePaths)
    3. try
    4. readOneProfile(···)
    5. openFile(···)
    6. if (!fs.existsSync(filePath))
    7. throw

    throw walks up this chain of constructs, until it finds a try statement. Execution continues in the catch clause of that try statement.

    1. throw «value»;

    Any value can be thrown, but it’s best to throw instances of Error:

    22.2.1. Options for creating error objects

    • Use class Error. That is less limiting in JavaScript than in a more static language, because you can add your own properties to instances:
    • Use one of JavaScript’s subclasses of Error (which are listed ).

    • Subclass Error yourself.

    1. class MyError extends Error {
    2. function func() {
    3. }
    4. MyError);

    The maximal version of the try statement looks as follows:

    1. try {
    2. } catch (error) {
    3. // finally_statements

    The try clause is mandatory, but you can omit either catch or finally (but not both). Since ECMAScript 2019, you can also omit (error), if you are not interested in the value that was thrown.

    22.3.1. The catch clause

    The following code demonstrates that the value that is thrown in line A is indeed caught in line B.

    1. const errorObject = new Error();
    2. throw errorObject; // (A)
    3. func();
    4. assert.equal(err, errorObject);

    22.3.2. The finally clause

    Let’s look at a common use case for finally: You have created a resource and want to always destroy it when your are done with it – no matter what happens while working with it. You’d implement that as follows:

    The finally is always executed – even if an error is thrown (line A):

    1. let finallyWasExecuted = false;
    2. () => {
    3. throw new Error(); // (A)
    4. finallyWasExecuted = true;
    5. Error
    6. assert.equal(finallyWasExecuted, true);

    The finally is always executed – even if there is a return statement (line A):

    1. function func() {
    2. return; // (A)
    3. finallyWasExecuted = true;
    4. }
    5. assert.equal(finallyWasExecuted, true);

    Quoting :

    • Error [root class]
      • RangeError: Indicates a value that is not in the set or range of allowable values.
      • ReferenceError: Indicate that an invalid reference value has been detected.
      • SyntaxError: Indicates that a parsing error has occurred.
      • TypeError: is used to indicate an unsuccessful operation when none of the other NativeError objects are an appropriate indication of the failure cause.
      • URIError: Indicates that one of the global URI handling functions was used in a way that is incompatible with its definition.

    22.4.1. Properties of error classes

    Consider err, an instance of Error:

    1. assert.equal(String(err), 'Error: Hello!');
    • .message: contains just the error message.
    • .stack: contains a stack trace. It is supported by all mainstream browsers.
    1. err.stack,
    2. Error: Hello!