Error Handling in Async Code
📖 Concept
Async error handling requires different patterns than synchronous code because errors can occur in callbacks, Promises, or async functions at different points in time.
Promise errors: Use .catch() at the end of chains
async/await errors: Use try/catch blocks
Global handlers: window.onunhandledrejection and window.onerror
🏠 Real-world analogy: Async error handling is like having contingency plans for a project. If Step 3 fails (API down), you need a plan B that doesn't crash the entire project. And you need a global "fire alarm" for any unhandled emergencies.
💻 Code Example
1// try/catch in async functions2async function loadUserData(userId) {3 try {4 const res = await fetch(`/api/users/${userId}`);5 if (!res.ok) throw new Error(`HTTP ${res.status}`);6 const user = await res.json();7 return user;8 } catch (error) {9 if (error instanceof TypeError) {10 console.error("Network error:", error.message);11 } else {12 console.error("API error:", error.message);13 }14 return null; // Graceful fallback15 }16}1718// Handling errors in Promise.all19async function fetchAll() {20 try {21 const results = await Promise.all([22 fetch("/api/1").then(r => r.json()),23 fetch("/api/2").then(r => r.json())24 ]);25 return results;26 } catch (err) {27 // One failure = all fail with Promise.all28 console.error("At least one request failed:", err);29 }30}3132// Better: Promise.allSettled for partial failures33async function fetchAllSafe() {34 const results = await Promise.allSettled([35 fetch("/api/1").then(r => r.json()),36 fetch("/api/2").then(r => r.json())37 ]);38 const successes = results.filter(r => r.status === "fulfilled").map(r => r.value);39 const failures = results.filter(r => r.status === "rejected").map(r => r.reason);40 return { successes, failures };41}4243// Error boundary pattern44async function withErrorBoundary(fn, fallback) {45 try {46 return await fn();47 } catch (error) {48 console.error("Caught:", error);49 return typeof fallback === "function" ? fallback(error) : fallback;50 }51}52const data = await withErrorBoundary(53 () => fetch("/api/data").then(r => r.json()),54 [] // Fallback value55);5657// Global error handlers58window.addEventListener("unhandledrejection", (event) => {59 console.error("Unhandled rejection:", event.reason);60 event.preventDefault(); // Prevent default console error61});
🏋️ Practice Exercise
Mini Exercise:
- Write an error boundary wrapper for async operations with retry
- Implement graceful degradation: try primary API, fall back to cache
- Create a centralized error logging system for async operations
- Build a circuit breaker pattern that stops calling a failing service
⚠️ Common Mistakes
Not catching errors in async functions — unhandled rejections crash Node.js and show warnings in browsers
Using try/catch around a function that returns a Promise without await — the catch won't work!
Not distinguishing network errors from HTTP errors —
fetchonly throws on network failuresCatching and silently swallowing errors with an empty catch block — always log or re-throw
Not cleaning up resources (event listeners, intervals) on error — use
finallyfor cleanup
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Error Handling in Async Code. Login to unlock this feature.