Understanding Errors and Exception Handling in JavaScript
No matter how carefully we write our code, errors are inevitable. Proper error handling is crucial to make applications stable, user-friendly, and easier to debug. JavaScript provides several powerful mechanisms for handling errors gracefully.
In this module, we’ll cover error types, handling techniques, and best practices.
Table of Contents
- Types of Errors in JavaScript
- What is Exception Handling?
- try…catch Statement
- The
throw
Keyword - Error Object and Custom Errors
- finally Block
- Asynchronous Error Handling
- Debugging with DevTools
- Best Practices
- Conclusion
1. Types of Errors in JavaScript
There are three major categories of errors:
- Syntax Errors – mistakes in code structure. javascriptCopyEdit
console.log("Hello" // SyntaxError: missing )
- Runtime Errors – occur during execution. javascriptCopyEdit
let a = b + 2; // ReferenceError: b is not defined
- Logical Errors – incorrect output, no error thrown. javascriptCopyEdit
function isEven(n) { return n % 2 === 1; // wrong logic! }
2. What is Exception Handling?
Exception handling is a way to deal with unexpected events during program execution without crashing the entire app.
3. try…catch Statement
The try...catch
block is used to catch and handle errors gracefully.
try {
let result = riskyFunction();
console.log(result);
} catch (error) {
console.error("Something went wrong:", error.message);
}
- Code in the
try
block is executed. - If an error occurs, control jumps to the
catch
block.
4. The throw
Keyword
You can throw custom errors using throw
.
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
try {
divide(5, 0);
} catch (e) {
console.error(e.message);
}
You can throw anything (strings, objects), but using the Error
constructor is recommended.
5. Error Object and Custom Errors
Built-in Error Types:
Error
SyntaxError
ReferenceError
TypeError
RangeError
Creating Custom Errors:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
throw new ValidationError("Invalid input");
6. finally Block
Code in finally
always runs, regardless of whether an error occurred.
try {
// code
} catch (e) {
// handle error
} finally {
console.log("Cleanup actions here");
}
Useful for closing resources, stopping loaders, etc.
7. Asynchronous Error Handling
In async functions or Promises, errors must be caught with .catch()
or try...catch
in async/await
.
Promises:
fetch("invalid-url")
.then(res => res.json())
.catch(error => console.error("Fetch failed:", error));
Async/Await:
async function loadData() {
try {
const res = await fetch("invalid-url");
const data = await res.json();
} catch (error) {
console.error("Error fetching:", error.message);
}
}
8. Debugging with DevTools
Modern browsers come with developer tools for debugging:
- Use breakpoints to pause execution.
- Watch variable values in real time.
- View call stack and scopes.
Add debugger;
in your code to trigger the debugger:
function test() {
let a = 5;
debugger;
console.log(a);
}
9. Best Practices
- Always validate user inputs.
- Avoid swallowing errors silently.
- Use meaningful error messages.
- Use
try...catch
wisely — not everywhere. - Prefer async/await with
try...catch
over.then().catch()
for readability. - Log errors for diagnostics, especially in production.
10. Conclusion
Robust error handling is vital for building professional JavaScript applications. It not only prevents crashes but also helps developers debug and improve UX.
In the next module, we’ll dive into JavaScript Modules (import/export) — a powerful way to organize and reuse code.