Error Handling in Async Code
Async operations can fail — network errors, server errors, invalid data. Proper error handling prevents your app from crashing and gives users meaningful feedback. Use try/catch with async/await.
Async Error Handling
- .catch() — For promise chains: fetchData().then().catch(err => ...)
- try/catch — For async/await: try { await fetch() } catch (err) { ... }
- finally — Runs always (success or failure): .finally() or try/catch/finally
- Error propagation — Unhandled promise rejections crash Node.js and show warnings in browsers
- Retry logic — Try again on failure: for loop with try/catch and delay
- Global handler — window.addEventListener('unhandledrejection', handler)
Async Error Handling Code
// Simulated failing API
function fetchData(shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) reject(new Error("Network error: Connection refused"));
else resolve({ data: "Success!", status: 200 });
}, 500);
});
}
// try/catch with async/await
async function getData() {
try {
const result = await fetchData(false);
console.log("✅ Data:", result.data);
} catch (error) {
console.error("❌ Error:", error.message);
} finally {
console.log("🔄 Done (runs always)");
}
}
getData();
// Retry with exponential backoff
async function fetchWithRetry(fn, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
console.log(`Attempt ${i + 1} failed: ${error.message}`);
if (i === retries - 1) throw error; // last attempt, rethrow
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// Error with user-friendly message
async function loadUserProfile(userId) {
try {
const user = await fetchData(userId < 0);
return user;
} catch (error) {
// Log for developers
console.error("API Error:", error);
// Show to users
throw new Error("Unable to load profile. Please try again later.");
}
}Tip
Tip
Always show loading states and handle errors in async operations. Users should never see a frozen UI. Display spinners during fetch, error messages on failure, and disable submit buttons to prevent double-submission.
Never swallow errors silently. Log, report, recover gracefully.
Common Mistake
Warning
Catching errors silently with empty catch blocks: catch(e) { }. This hides bugs. Always log the error, show a user-friendly message, or re-throw if you can't handle it meaningfully at that level.
Practice Task
Note
Error handling: (1) Wrap a fetch call in try/catch with specific error messages for network vs HTTP errors. (2) Implement a retry mechanism (3 attempts with delay). (3) Show loading/error/success states.
Quick Quiz
Key Takeaways
- Async operations can fail — network errors, server errors, invalid data.
- .catch() — For promise chains: fetchData().then().catch(err => ...)
- try/catch — For async/await: try { await fetch() } catch (err) { ... }
- finally — Runs always (success or failure): .finally() or try/catch/finally