A program that runs perfectly on your machine can still fail on a user's device: a server that does not answer, a malformed response, a missing file. These situations are not bugs to fix, they are events to plan for.
That is exactly what try is for: marking the part of the code where an error is expected, so the program knows what to do instead of stopping dead.
Definition
try opens a watched block. If an error is raised inside it, JavaScript immediately abandons the rest of the block and jumps to the catch that follows, handing it the error object.
try {
const data = JSON.parse('{ "price": ');
console.log(data.price); // this line is never reached
} catch (error) {
console.log("Unreadable response:", error.message);
}A try never lives alone. It has to be followed by a catch, a finally, or both in that order. Written on its own it produces a syntax error.
What it actually watches
The watch covers the immediate execution of the block, including any function called from that block, however deep the call stack goes.
| Situation | Caught by the try |
|---|---|
| Error raised by a line of the block | Yes |
| Error raised inside a function called by the block | Yes |
| Error inside a function handed to setTimeout | No |
| Promise rejected without await | No |
Those last two rows explain the vast majority of blocks that appear to do nothing. By the time the deferred function runs, the block finished its work long ago. A rejected Promise is caught by putting the wait inside the block, or through its own catch() method.
A syntax error in the file can never be caught by a try: it is detected while parsing, before the first line runs.
A narrow block beats a wide one
The temptation is to wrap thirty lines in a single try and consider the matter settled. The result is a net that also catches programming mistakes: a misspelled property name, a call to a method that does not exist. Those errors deserve to surface and be fixed, not to be absorbed in silence.
const received = "{ this is not JSON }";
let config;
try {
config = JSON.parse(received);
} catch {
config = { theme: "light" };
}
console.log(config.theme); // "light"Here the block wraps only the call that can genuinely fail, and the rest of the work is written normally, outside. This split has a second benefit: the catch knows what failed, so it can decide something useful.
Frequently asked questions
Does a try slow the code down?
As long as no error is raised, the cost is negligible in an ordinary application. What genuinely costs something is the raising itself: the engine has to build an Error object and capture the call stack. A loop that fails on every pass is worth avoiding, but the block itself is not the problem.
Can several try blocks be nested?
Yes, and it is common as soon as the recovery step can itself fail. The nearest block handles the error first. If it does not know what to do with it, it re-raises it with throw and the error carries on toward the enclosing block. Still, avoid stacking three levels: beyond two, the path of an error becomes very hard to follow.
Should every network call be wrapped in one?
Every call whose failure changes what the page does, yes. A network call fails for reasons that have nothing to do with the code: a dropped connection, an unavailable server, a timeout. Without a watched block, those cases surface as unhandled errors and the interface stays frozen on a loading screen.