Technology · JavaScript
JavaScript Try Catch
Handle errors safely with try-catch blocks, finally, error types, and custom error handling patterns.
TL;DR
- 01Wrap risky code in try block to catch errors.
- 02Use catch to handle errors without crashing the app.
- 03Use finally to run cleanup code no matter what.
Basic Try-Catch
- Wrap code that might throw errors inside a try block.
try { const result = riskyFunction(); console.log(result); } catch (error) { console.error("Something went wrong:", error); } - The catch block runs if any error is thrown inside the try.
- The error variable contains the error object with details.
- Code after the error inside try is not executed.
try { throw new Error("Oops"); console.log("This never runs"); } catch (e) { console.log(e.message); // "Oops" } - Try-catch prevents the entire program from crashing.
Error Types
- JavaScript has several built-in error types for different situations.
try { JSON.parse("invalid json"); } catch (error) { if (error instanceof SyntaxError) { console.log("JSON parsing failed"); } } - Common errors:
Error,SyntaxError,TypeError,ReferenceError,RangeError. TypeErroroccurs when a value is the wrong type.try { const x = null; x.method(); // TypeError: Cannot read property 'method' of null } catch (e) { console.log(e instanceof TypeError); // true }ReferenceErrorhappens when a variable doesn't exist.- Check error types to handle different situations differently.
Finally Block
- Use finally to run code that always executes, error or not.
try { const file = openFile("data.txt"); processFile(file); } catch (error) { console.error("Processing failed"); } finally { closeFile(file); } - Finally runs even if there's no error in the try block.
- Finally runs even if you return early from the catch.
function getData() { try { return fetchData(); } finally { cleanup(); } } - Use finally for cleanup like closing files, releasing resources.
- Reset UI state such as loading spinners inside a finally block.
setLoading(true); try { await fetchData(); } finally { setLoading(false); // always turns off the spinner } - Combine try, catch, and finally together for full error coverage.
let connection; try { connection = openDB(); return connection.query('SELECT * FROM users'); } catch (error) { console.error('Query failed:', error.message); } finally { connection?.close(); }
Custom Error Handling
- Throw custom error messages for better clarity.
try { if (!user) { throw new Error("User not found"); } } catch (error) { console.error(error.message); } - Create custom error classes for specific situations.
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } } try { if (age < 0) throw new ValidationError("Age must be positive"); } catch (e) { if (e instanceof ValidationError) { console.log("Validation failed:", e.message); } } - Include relevant context in error messages for debugging.
- Use stack traces to understand where errors originated.
Error Handling Patterns
- Catch specific errors and provide fallback behavior.
try { const config = JSON.parse(configString); } catch (error) { const config = getDefaultConfig(); } - Re-throw errors after logging for upstream handling.
try { operation(); } catch (e) { logger.error(e); throw e; } - Use multiple catch blocks for different error types.
try { riskyCode(); } catch (error) { if (error instanceof TypeError) { // handle type error } else if (error instanceof SyntaxError) { // handle syntax error } else { // handle unknown error } } - Log errors with context to help with debugging later.
Tip: Always include a finally block when managing resources like file handles or database connections to ensure cleanup happens.
Warning: Catching all errors with a bare catch can hide bugs, so check error types and only handle expected errors gracefully.