How do you handle errors in asynchronous operations?
Interview answer
Handle promise failures with .catch(), use try/catch around awaited work, check response.ok for HTTP errors, and choose Promise.allSettled when parallel tasks must fail independently. In React, keep loading, error, and data states explicit and cancel obsolete requests with AbortController.
The short answer
For promises, use .catch() at the end of the chain. For async/await, use try...catch. For multiple parallel operations, use Promise.allSettled to handle failures individually. In React, use error boundaries for rendering errors and try...catch inside useEffect for async errors.
With promise chains
1fetch('/api/users')2 .then((response) => {3 if (!response.ok) {4 throw new Error(`HTTP error: ${response.status}`);5 }6 return response.json();7 })8 .then((data) => {9 console.log(data);10 })11 .catch((error) => {12 console.log('Something went wrong:', error.message);13 });
A single .catch() at the end handles errors from any step in the chain. If the network fails, the response is not OK, or the JSON parsing fails — all of these end up in the .catch().
With async/await
1async function getUsers() {2 try {3 const response = await fetch('/api/users');4 if (!response.ok) {5 throw new Error(`HTTP error: ${response.status}`);6 }7 const data = await response.json();8 return data;9 } catch (error) {10 console.log('Failed to fetch users:', error.message);11 return [];12 }13}
Handling multiple async operations
Promise.all — fails fast (one failure cancels everything):
1try {2 const [users, posts] = await Promise.all([3 fetch('/api/users').then((r) => r.json()),4 fetch('/api/posts').then((r) => r.json()),5 ]);6} catch (error) {7 // If either request fails, this catches it8 console.log('One of the requests failed:', error.message);9}
Promise.allSettled — get results from all, even if some fail:
1const results = await Promise.allSettled([2 fetch('/api/users').then((r) => r.json()),3 fetch('/api/posts').then((r) => r.json()),4]);56results.forEach((result) => {7 if (result.status === 'fulfilled') {8 console.log('Success:', result.value);9 } else {10 console.log('Failed:', result.reason);11 }12});
Promise.allSettled never rejects. Each result has a status of either 'fulfilled' or 'rejected'.
In React components
1function UserList() {2 const [users, setUsers] = useState([]);3 const [error, setError] = useState(null);4 const [loading, setLoading] = useState(true);56 useEffect(() => {7 const controller = new AbortController();89 fetch('/api/users', { signal: controller.signal })10 .then((response) => {11 if (!response.ok)12 throw new Error('Failed to fetch');13 return response.json();14 })15 .then((data) => {16 setUsers(data);17 setLoading(false);18 })19 .catch((error) => {20 if (error.name !== 'AbortError') {21 setError(error.message);22 setLoading(false);23 }24 });2526 return () => controller.abort();27 }, []);2829 if (loading) return <p>Loading...</p>;30 if (error) return <p>Error: {error}</p>;31 return (32 <ul>33 {users.map((u) => (34 <li key={u.id}>{u.name}</li>35 ))}36 </ul>37 );38}
Common Pitfalls
A common mistake is not checking response.ok after a fetch call. fetch only rejects on network errors (like no internet). A 404 or 500 response does not throw — it resolves with response.ok set to false. You must check this manually and throw if the response is not OK.
Interview Tip
Cover three patterns: .catch() with promises, try...catch with async/await, and Promise.allSettled for parallel operations. The fetch gotcha about response.ok is a great thing to mention because many candidates do not know about it. If the interview is React-focused, show the loading/error/data state pattern.
Why interviewers ask this
Error handling in async code is where real bugs happen. Network requests fail, APIs return errors, and timeouts occur. Interviewers want to see if you handle these cases gracefully instead of letting the app crash. A candidate who knows about response.ok, Promise.allSettled, and React error states shows they build reliable applications.
A production-ready async error checklist
A strong interview answer goes beyond catching an exception. Explain which failures you expect, what the user sees, and whether the operation is safe to retry.
- Separate network failures, non-2xx HTTP responses, parsing errors, and user cancellation.
- Preserve the original error or cause so logs remain useful after adding a user-friendly message.
- Retry only transient failures, use backoff, and never retry an unsafe mutation blindly.
- Prevent stale requests from overwriting newer React state by aborting or ignoring obsolete work.
Related interview practice
Related React Ecosystem interview topics
Continue with nearby questions from the same topic cluster.