Error Handling in Async Code0%

Error Handling in Async Code

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Error Handling in Async Code in Modern JavaScript

Asynchronous operations represent the most common source of runtime failures in web applications—from intermittent network drops to API rate limits and aborted timeouts. Understanding how errors propagate across Promises, async/await, and event loops is essential for production stability.


1. Async/Await with try...catch

The cleanest and most readable pattern for handling asynchronous errors is wrapping await calls in standard try...catch blocks:

JavaScript
async function fetchUserDashboard(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`API returned HTTP status ${response.status}`);
}
 
const userData = await response.json();
return userData;
} catch (error) {
console.error(`Dashboard fetch failed for user ${userId}:`, error.message);
// Return fallback state or rethrow
return { error: true, message: 'Unable to load dashboard data' };
}
}

2. The Unhandled Promise Rejection Hazard

If a Promise is rejected and has no .catch() handler and is not caught inside a try...catch, JavaScript triggers an UnhandledPromiseRejection:

Output
Rejected Promise
│
Is there a handler?
┌────────┴────────┐
YES NO
│ │
▼ ▼
.catch() / UnhandledPromiseRejection
try...catch (Node.js process terminates /
Browser logs red console error)

Global Safety Nets

Always register global handlers to capture any stray unhandled rejections:

JavaScript
// In Modern Browsers:
window.addEventListener('unhandledrejection', (event) => {
console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
// Log to error tracking service (e.g. Sentry)
event.preventDefault(); // Suppress default browser error log
});
 
// In Node.js:
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

3. Go-Style Tuple Pattern for Clean Async Handling

Some developers prefer avoiding nested try...catch blocks by wrapping async operations in a helper that returns a [data, error] tuple (inspired by Go):

JavaScript
async function to(promise) {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
 
// Usage:
async function loadFeed() {
const [data, err] = await to(fetchFeedFromNetwork());
if (err) {
console.error('Failed to load feed:', err);
return;
}
console.log('Feed loaded:', data);
}

4. Error Handling with Promise.allSettled()

When running multiple independent operations in parallel, Promise.all() fails fast if any single promise rejects. To inspect every outcome and handle errors individually, use Promise.allSettled():

JavaScript
const userIds = [1, 2, 9999, 4]; // 9999 is an invalid ID
 
const results = await Promise.allSettled(
userIds.map(id => fetchUserById(id))
);
 
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`User ${userIds[index]} loaded:`, result.value.name);
} else {
console.warn(`User ${userIds[index]} failed:`, result.reason.message);
}
});

Interactive Quiz

Practice Quiz

Question 1
What happens in modern Node.js environments when a Promise rejection is left unhandled?
Question 2
What browser event fires whenever a Promise rejects without a local catch handler?
Question 3
Why is Promise.allSettled() preferred over Promise.all() when fetching data for independent dashboard widgets?
Question 4
In an async function, how does a rejected Promise inside an await expression manifest?
Question 5
What property on each result object of Promise.allSettled() indicates whether the operation succeeded?
Next Lesson

What are Modules?

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Previous LessonNext Lesson
Throwing Custom ErrorsWhat are Modules?

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext