Q1What does try { console.log(1); } finally { console.log(2); } print?
Catching Exceptions — try / catch / finally
Catch runtime exceptions with try and catch, run cleanup in finally, rethrow with throw, and branch by error type.
When you use JSON.parse to turn a received string back into an object, a single wrong character stops execution on that line, and the display and save code you wrote below it never runs. You can't rule out every possible cause ahead of time with if checks, either.
This article covers try / catch, which catches the exception that stopped your code, and finally, which always runs.
Catching an Exception — try and catch
Say you want to parse a settings string received from a server and apply it to the page. If the string is malformed, JSON.parse throws an exception (an error that interrupts processing) on that line, and none of the lines below it run. The page stays blank, and nothing else happens.
When an exception is thrown inside try (a block wrapping code that might fail), execution moves to catch (a block that receives the exception). The error in catch (error) is simply the name you give the received value (err or any other name works just as well). It holds name, the name of the error type, and message, a description of what went wrong.
// A string with a leftover trailing comma
const configText = '{"theme":"dark","fontSize":14,}';
try {
const config = JSON.parse(configText);
console.log(config.theme); // this line doesn't run
} catch (error) {
// execution jumps here when an exception is thrown
console.log("Couldn't load the settings"); // Couldn't load the settings
console.log(error.name); // SyntaxError
}
// thanks to the catch, the lines after it still run
console.log("Showing the default settings"); // Showing the default settings
Once execution moves to catch, it never goes back to finish the line that threw, so put your fallback display or default value inside catch. The wording of message varies between browsers, so check name when you need to tell error types apart.
Running Code Whether It Succeeds or Fails — finally
Cleanup such as hiding a loading indicator or closing an open connection is needed whether the operation succeeds or fails. If you write the same line in both try and catch, you may later fix one copy and forget the other, and the two paths stop behaving the same.
finally (a block that always runs when leaving try) goes after catch. Whether try ran to the end or catch received an exception, it always runs just before leaving. You can also leave out catch and write only try and finally.
function loadReport(text) {
try {
const report = JSON.parse(text);
console.log(report.title);
} catch (error) {
console.log("Failed to load");
} finally {
// always runs when leaving try
console.log("Loading indicator hidden");
}
}
// On success
loadReport('{"title":"September sales"}'); // September sales → Loading indicator hidden
// On failure
loadReport('{"title":}'); // Failed to load → Loading indicator hidden
finally runs after the code in try or catch has finished. That's why, in the code above, the cleanup message appears after the title on success and after the failure message on failure.
Variables Declared Inside try Can't Be Read Outside It
A variable declared with const or let inside try can only be used inside that block. Reading it from catch, finally, or any line after the whole statement throws a ReferenceError. Declare values you'll need later with let before try, and only assign to them inside try.
Letting the Caller Know — throw Inside catch
Say a function that reads booking data catches an exception and only logs it. The function then returns normally, so the caller never notices the failure, keeps using the return value, and stops again on some other line far from the actual cause.
Passing the error that catch received straight to throw (a statement that throws an exception) sends the same exception on to the caller. The function can log the failure itself and still leave it to the caller to decide how to respond.
function readEntry(text) {
try {
return JSON.parse(text);
} catch (error) {
console.log(`Can't read the booking data: ${error.name}`);
throw error; // rethrow the received exception as-is
}
}
try {
const entry = readEntry('{"name":"Bob","seats":}');
console.log(entry.name); // this line doesn't run
} catch (error) {
console.log("Booking canceled"); // Booking canceled
}
If the caller also needs to respond to an exception caught inside a function, rethrow it by writing throw error; at the end of catch. Lines after throw don't run, so put your logging line before it.
An Empty catch Makes the Failure Disappear
Leaving catch (error) { } empty makes the exception seem to vanish, but the failure isn't recorded anywhere, so you can't trace the cause later. The most common result is a page that just stays blank. Always do at least one of these: show a message, fall back to a default value, or rethrow.
Branching by Error Type — instanceof and name
catch receives every exception, whatever its type. But the message you want to show users depends on whether the data was malformed or a value you tried to read was missing.
instanceof (an operator that checks whether the value on the left was created from the type on the right) lets you check the type, as in error instanceof SyntaxError. error.name holds the type's name as a string, too.
const applicant = { name: "Alice", contact: null };
try {
console.log(applicant.contact.email);
} catch (error) {
if (error instanceof SyntaxError) {
console.log("The data format is invalid"); // doesn't get here
} else if (error instanceof TypeError) {
console.log("The value you tried to read is missing"); // The value you tried to read is missing
} else {
console.log(`Unexpected error type: ${error.name}`);
}
console.log(error.name); // TypeError
}
catch (error), but each has a different name. Checking the type lets you show different messages.Exceptions that match none of the checks are all caught by the final else. The table below lists four types you'll run into often and when each one is thrown.
| Type | When it's thrown | Example code that throws it |
|---|---|---|
| SyntaxError | The string isn't in JSON format | JSON.parse("{,}") |
| TypeError | You read a property of null or undefined | applicant.contact.email |
| RangeError | You passed a value outside the allowed range, such as a digit count | (1980).toFixed(200) (digits go up to 100) |
| ReferenceError | You read a name that was never declared | console.log(total) |
Knowledge Check
Answer each question one by one.
Q2If a function's catch only logs and doesn't include throw error;, what return value does the caller get?
Q3Which operator do you use to check the type of the error that catch received?