try, catch, and finally
Objectives
By the end of this lesson, you should be able to:
- Explain what happens when an error is thrown and nothing catches it
- Wrap risky code in
try/catchand read the error’smessage - Use
finallyto run cleanup code whether or not an error happened
💡 Why this matters: Right now, one bad input can crash your entire program. A user pastes malformed data, a network response isn’t what you expected, a method gets called on something that turns out to be
undefined, and the whole script stops.try/catchlets you decide what happens next instead of letting JavaScript decide for you.
An Error With Nothing to Catch It
Here’s a small program with a bug: it calls sayHello() on an object that doesn’t have that method.
const user = { name: "Priya" };
user.sayHello();
console.log("This never runs");
Running this crashes immediately:
TypeError: user.sayHello is not a function
Notice the last line, console.log("This never runs"), never runs. Once an error is thrown and nothing catches it, JavaScript stops executing the rest of the script right there.
try and catch
Wrap the risky line in a try block, and put a catch block right after it. If anything inside try throws an error, execution jumps straight to catch instead of crashing the program.
const user = { name: "Priya" };
try {
user.sayHello();
} catch (error) {
console.log("Caught an error:", error.message);
}
console.log("The program keeps running");
Output:
Caught an error: user.sayHello is not a function
The program keeps running
The error object passed into catch always has a .message property describing what went wrong. Because the error was caught, the line after the try/catch block runs normally.
A Second Example: JSON.parse
JSON.parse is a common source of real runtime errors: it throws whenever the text it’s given isn’t valid JSON.
const data = "{ bad json";
try {
const parsed = JSON.parse(data);
console.log(parsed);
} catch (error) {
console.log("Caught an error:", error.message);
}
Output:
Caught an error: Expected property name or '}' in JSON at position 2 (line 1 column 3)
The exact wording of error.message can vary slightly between JavaScript engines, but the important part is the same everywhere: the program didn’t crash, and you got a description of what went wrong.
finally: Code That Always Runs
A finally block runs after try/catch, no matter what happened: whether the code succeeded, or an error was thrown and caught. It’s the right place for cleanup work, like closing a connection or logging that an operation finished.
function parseConfig(text) {
try {
const config = JSON.parse(text);
console.log("Parsed config:", config);
return config;
} catch (error) {
console.log("Failed to parse config:", error.message);
return null;
} finally {
console.log("finally: parseConfig is done running");
}
}
console.log("--- valid input ---");
parseConfig('{"theme": "dark"}');
console.log("--- invalid input ---");
parseConfig("{ not valid json");
Output:
--- valid input ---
Parsed config: { theme: 'dark' }
finally: parseConfig is done running
--- invalid input ---
Failed to parse config: Expected property name or '}' in JSON at position 2 (line 1 column 3)
finally: parseConfig is done running
Look at the last line of each block: finally: parseConfig is done running prints both times, once after the successful parse and once after the failed one. That’s the whole point of finally: it doesn’t care whether try succeeded or catch ran, it runs either way.
Try It
Predict the output first, then run this in your own console (browser devtools or node) to check:
function divide(a, b) {
try {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
console.log("Result:", a / b);
} catch (error) {
console.log("Error:", error.message);
} finally {
console.log("divide() finished");
}
}
divide(10, 2);
divide(10, 0);
Check that divide() finished prints after both calls, even though only the second one hits the catch block.
Recap
- An uncaught error stops your entire program; nothing after it runs.
trywraps risky code;catch (error)runs if that code throws, anderror.messagedescribes what happened.finallyalways runs aftertry/catch, whether or not an error occurred, making it the right spot for cleanup.
Next lesson, you’ll raise your own errors on purpose with throw, and build a custom error class so callers can tell exactly what kind of problem occurred.