No matter how carefully you write code, things fail at runtime — a user types invalid data, an API call errors out, a property doesn't exist on the object you expected. Without handling it, an uncaught error crashes your script (or at least that execution path) and prints a stack trace to the console. JavaScript's try...catch statement lets you catch that failure and decide what to do instead of letting it blow up.
Basic try...catch Syntax
try {
const result = riskyOperation(); // may throw
console.log("Success:", result);
} catch (error) {
console.log("Something went wrong:", error.message);
}
function riskyOperation() {
throw new Error("This operation failed");
}
Output:
Something went wrong: This operation failed
Code inside try runs normally until something throws. The moment it does, execution jumps straight to catch, skipping any remaining lines in the try block.
The Error Object
The value caught in catch (error) is usually an Error object (or a subclass of it), with a few standard properties worth knowing: error.message (a human-readable description) and error.name (the error type, e.g. "TypeError", "SyntaxError", "RangeError").
try {
JSON.parse("{ this is not valid JSON }");
} catch (error) {
console.log(error.name); // SyntaxError
console.log(error.message); // describes exactly what's wrong
}
Output:
SyntaxError
Unexpected token t in JSON at position 2
JSON.parse() on malformed input is one of the most common real-world places try...catch shows up — you can't know in advance whether a string of JSON coming from a user, a file, or an API is actually valid.
The finally Block
An optional finally block runs after try/catch no matter what happened — whether the try succeeded, an error was caught, or even if the catch block itself re-threw. It's the right place for cleanup code that must always run, like closing a connection or hiding a loading spinner.
function processData(input) {
console.log("Loading...");
try {
const data = JSON.parse(input);
console.log("Parsed:", data);
} catch (error) {
console.log("Parse failed:", error.message);
} finally {
console.log("Done loading.");
}
}
processData("not json");
Output:
Loading...
Parse failed: Unexpected token o in JSON at position 1
Done loading.
try...catch with async/await
Promises rejected inside an async function can be caught with the exact same syntax, which is one of the biggest readability wins of async/await over chained .then()/.catch():
async function loadUser() {
try {
const response = await fetch("/api/user/1");
return await response.json();
} catch (error) {
console.log("Failed to load user:", error.message);
}
}
try...catch only catches errors in code it directly wraps — it won't catch an error thrown later inside a setTimeout callback, for instance, since that code runs on its own turn through the event loop, outside the original try block entirely.
