throw
try
statement
try
blockcatch
clausefinally
clauseThis chapter covers how JavaScript handles exceptions.
Why doesn’t JavaScript throw exceptions more often?
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 Profile
:
function readProfiles(filePaths) {
const profiles = [];
for (const filePath of filePaths) {
try {
const profile = readOneProfile(filePath);
profiles.push(profile);
} catch (err) { // (A)
console.log('Error in: '+filePath, err);
}
}
}
function readOneProfile(filePath) {
const profile = new Profile();
const file = openFile(filePath);
// ··· (Read the data in `file` into `profile`)
return profile;
}
function openFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error('Could not find file '+filePath); // (B)
}
// ··· (Open the file whose path is `filePath`)
}
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:
throw
statement to indicate that there was a problem.try-catch
statement to handle the problem.When we throw, the following constructs are active:
readProfiles(···)
for (const filePath of filePaths)
try
readOneProfile(···)
openFile(···)
if (!fs.existsSync(filePath))
throw
One by one, throw
exits the nested constructs, until it encounters a try
statement. Execution continues in the catch
clause of that try
statement.
throw
This is the syntax of the throw
statement:
Any value can be thrown, but it’s best to throw an instance of Error
or its subclasses.
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 later).
Subclass Error
yourself.
try
statementThe maximal version of the try
statement looks as follows:
You can combine these clauses as follows:
try-catch
try-finally
try-catch-finally
Since ECMAScript 2019, you can omit the catch
parameter (error)
, if you are not interested in the value that was thrown.
try
blockThe try
block can be considered the body of the statement. This is where we execute the regular code.
catch
clauseIf an exception reaches the try
block, then it is assigned to the parameter of the catch
clause and the code in that clause is executed. Next, execution normally continues after the try
statement. That may change if:
return
, break
or throw
inside the catch
block.finally
clause (which is always executed before the try
statement ends).The following code demonstrates that the value that is thrown in line A is indeed caught in line B.
const errorObject = new Error();
function func() {
throw errorObject; // (A)
}
try {
func();
} catch (err) { // (B)
assert.equal(err, errorObject);
}
finally
clauseThe code inside the finally
clause is always executed at the end of a try
statement – no matter what happens in the try
block or the catch
clause.
Let’s look at a common use case for finally
: You have created a resource and want to always destroy it when you are done with it – no matter what happens while working with it. You’d implement that as follows:
const resource = createResource();
try {
// Work with `resource`. Errors may be thrown.
} finally {
resource.destroy();
}
finally
is always executedThe finally
clause is always executed – even if an error is thrown (line A):
let finallyWasExecuted = false;
assert.throws(
() => {
try {
throw new Error(); // (A)
} finally {
finallyWasExecuted = true;
}
},
Error
);
assert.equal(finallyWasExecuted, true);
And even if there is a return
statement (line A):
let finallyWasExecuted = false;
function func() {
try {
return; // (A)
} finally {
finallyWasExecuted = true;
}
}
func();
assert.equal(finallyWasExecuted, true);
Error
is the common superclass of all built-in error classes. It has the following subclasses (I’m quoting the ECMAScript specification):
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.Consider err
, an instance of Error
:
Two properties of err
are especially useful:
.message
: contains just the error message.
.stack
: contains a stack trace. It is supported by all mainstream browsers.
Exercise: Exception handling
exercises/exception-handling/call_function_test.mjs
Quiz
See quiz app.