A
But a crash is not inevitable. When something goes wrong at runtime the language doesn't just quit silently — it raises an exception: a special "something has gone wrong here!" signal that shoots up through your program looking for someone to deal with it. If nobody does, it reaches the top and the program dies. Exception handling is how you volunteer to catch that signal and respond gracefully — recover, or at least fail cleanly — instead of letting the program fall over.
This is the difference between an app that pops up "Sorry, that file couldn't be opened — try another?" and one that vanishes without warning. Real, robust programs are full of exception handling, because the world they run in is full of things that can go wrong: bad input, missing files, dropped network connections, disks that fill up. A professional program expects trouble and plans for it.
try and catch
The tool has two halves. You put the code that might go wrong inside a try
block, and the code that says what to do if it does inside a
catch block that follows it. Think of it as a safety net strung under a
tightrope walker: the walker (your risky code) tries to cross; if they fall, the net (the
catch) catches them instead of letting them hit the ground.
Here is a program that reads a number the user "typed". If it isn't a number, converting it fails and an exception is raised. Without a net the program would crash; with one, it catches the exception and prints a friendly message instead. Press Run:
Notice the last line still prints. That is the whole point: the exception was handled, so the
program didn't crash — it recovered and continued. The moment something inside try throws,
the rest of the try block is skipped and control jumps straight into catch,
where err holds the exception that was raised (usually an Error object with a
.message).
You don't have to wait for the language to spot trouble — you can raise an exception
yourself with throw new Error("…"). This is how you signal that
your own rules have been broken: an argument that makes no sense, a value out of range, a
state that should never happen. Throwing is like pulling a fire alarm — it stops what you're doing
immediately and hands the problem to whoever is prepared to deal with it.
A classic example is a divide function that refuses to divide by zero. Rather than return a
meaningless result, it throws — and the caller wraps the call in a try/catch:
The middle pair (7 / 0) throws, so its message is caught and printed, and the loop simply
moves on to the next pair. One bad case no longer sinks the whole batch. Notice how throw
and catch work as a pair across a distance: the alarm is pulled deep inside
divide, but it's answered up in the loop. The exception travels up the chain of calls
until it finds a catch ready for it.
A tempting shortcut is to signal failure with a special return value — -1,
null, or "ERROR". The trouble is that nothing forces the caller to
check it. They can quietly use the -1 as if it were a real answer, and now you have
a silent
finally — the code that always runs
There is a third, optional block: finally. Whatever happens — whether the
try succeeded, whether it threw and was caught, even whether you returned out
of the middle — the finally block runs. Always. It's the "no matter what, do this
on the way out" block.
What's it for? Cleanup. When you open a file, a database connection or a network
socket, you must close it again — and you must close it even if something failed while you were
using it, otherwise you leak resources. finally is the guaranteed place to put that
closing step. Run this and watch the order the lines print in for both a good and a bad value:
For the good value you see open → process → close. For the bad value you see open → problem →
close. Either way the file is closed. That guarantee is exactly why finally
exists: the cleanup can't be skipped, forgotten, or jumped over by an error.
A full handler can use all three blocks. Read this as a sentence: "try to do the risky thing; if it goes wrong, catch the problem and deal with it; and finally, whatever happened, tidy up afterwards."
try — wrap the code that could fail.catch — respond to the exception: recover, retry, log it, or show a
friendly message. Runs only when something was thrown.finally — cleanup that must happen regardless. Optional, but priceless
for releasing resources.throw new Error("…") — raise your own exception when your rules are
broken, so callers can catch it.
The most damaging habit in exception handling is silently swallowing exceptions — an
empty catch that does nothing:
This looks tidy, but it's a trap. The program no longer crashes, so you feel safe — yet the
bug is still there, now completely hidden. You've turned a loud, informative
runtime error into a silent
The rule: only catch what you can genuinely handle. If you know what to do about a
bad file — ask for another — then catch it and do that. If you don't actually know how to recover,
don't muffle it: at the very least log or report it (console.log the
message), or let it travel up to code that can deal with it. And remember that
finally runs whether or not an error occurred — so cleanup belongs
there, never buried in an empty catch that pretends nothing happened.