Featured
- Get link
- X
- Other Apps
⚠️ Day 23 — Error Handling & Try...Catch
⚠️ Day 23 — Error Handling & Try...Catch
Hey Guys Errors happen — network failures, bad data, and unforeseen edge cases. Today we’ll learn robust patterns to catch, classify, and recover from errors in JavaScript, both in synchronous code and in asynchronous flows. This makes your apps resilient and your users happier.
🔎 Why error handling matters
When something fails in production, it should fail gracefully. If an app throws an uncaught error, the UI may break, data may be lost, or security issues may surface. Proper error handling helps you:
- Provide clear, actionable messages to users.
- Log errors for debugging and monitoring (Sentry, LogRocket, server logs).
- Retry or fallback when appropriate (retry network calls, show cached data).
- Prevent cascading failures across app components.
⚙ Basic try / catch / finally
The `try` block contains code that may throw. The `catch` block handles the error. The `finally` block runs regardless — ideal for cleanup tasks.
try {
const data = JSON.parse(someString);
// use data
} catch (err) {
console.error('Parsing failed:', err.message);
// show friendly UI message
} finally {
// cleanup, remove loading indicators
}
🧩 Throwing custom errors
Throw meaningful errors with names/types to let higher-level code decide how to handle them.
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
function validateUser(user) {
if (!user.email) throw new ValidationError('Email required', 'email');
}
Catchers can then use `instanceof` or `err.name` to implement targeted recovery.
📡 Handling fetch() and network errors
`fetch` does not reject on HTTP error status — it only rejects on network failure. Always check `response.ok` and throw accordingly.
async function safeFetch(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
const data = await res.json();
return { ok: true, data };
} catch (err) {
return { ok: false, error: err };
}
}
const result = await safeFetch('/api/data');
if (!result.ok) {
// show fallback UI, log error
}
🔁 Retry strategies
For transient network issues, retrying with backoff often fixes the problem. Use exponential backoff to avoid hammering the server.
async function retry(fn, attempts = 3, delay = 500) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
await new Promise(r => setTimeout(r, delay * Math.pow(2, i)));
}
}
}
Use this for non-idempotent operations only when safe or for GET requests where retrying is fine.
🛡 Security & error messages
- Show friendly messages to users — do not expose raw stack traces.
- Log detailed errors to a secure backend or third-party monitoring tool.
- Avoid including sensitive data in error messages (tokens, PII).
📦 Centralized error handling patterns
Create a small error handler module that decides whether an error is recoverable, how to present it, and whether to send it to telemetry.
function handleError(err, context = {}) {
// classify error
if (err instanceof ValidationError) {
showFieldError(err.field, err.message);
return;
}
// network error
if (err.message && err.message.startsWith('HTTP')) {
showToast('Network problem. Please try again.');
}
// log detailed info to server
sendErrorToServer({err, context});
}
🧠 Async error handling – best practices
- Prefer `try/catch` inside `async` functions to handle errors locally when you can recover.
- Use a safe wrapper pattern to avoid repetitive try/catch blocks:
const to = p => p.then(v => [null, v]).catch(e => [e, null]);
// usage:
const [err, data] = await to(fetch('/api').then(r => r.json()));
if (err) handleError(err);
🧪 Testing error paths
Unit tests should simulate failures to verify recovery flows. Use mock fetches, thrown errors, and network timeouts in tests.
📈 Observability — logging & monitoring
Send errors to a logging service with context — user id, route, recent actions. Monitoring helps detect spikes and regressions quickly.
⚠️ Common pitfalls & how to avoid them
- Swallowing errors silently — always log or report them.
- Showing raw errors to users — sanitize messages.
- Retrying non-idempotent operations carelessly — consider side effects.
- Mixing UI errors and program state — keep concerns separate.
📝 Practice tasks (realistic)
- Wrap a third-party fetch in a `safeFetch` and implement retry+backoff.
- Create a `ValidationError` and ensure forms display field-level messages when thrown.
- Simulate network timeouts and confirm your UI shows cached content or a retry button.
🎯 Summary
Error handling is not optional — it’s a feature. By classifying errors, providing graceful fallbacks, logging to observability tools, and using retry strategies carefully, you make apps reliable and maintainable. Practice these patterns today to build production-grade front-end systems.
Popular Posts
💙 Day 27: Bootstrap Mini Project – Responsive Portfolio Page
- Get link
- X
- Other Apps
🎨 Day 2: Headings, Paragraphs & Text Formatting
- Get link
- X
- Other Apps
Comments
Post a Comment