async/await makes asynchronous code readable, but error handling still requires deliberate structure.
Scope your try/catch blocks
Wrap only the operation that can fail, not the entire function body. This keeps failures easier to debug.
async function loadUser(userId) {
let profile;
try {
profile = await fetchProfile(userId);
} catch (err) {
throw new Error('Profile request failed');
}
return buildViewModel(profile);
}
Differentiate expected vs unexpected errors
Expected errors (like 404 or validation failures) should return user-friendly feedback. Unexpected errors should be logged with context.
Add retries carefully
Use retries only for transient failures such as network timeouts. Add exponential backoff to avoid retry storms.
Use centralized monitoring
Capture runtime errors with request IDs, user context, and endpoint info. Better observability shortens incident resolution time.
Good async code is not only readable. It is predictable under failure.