Skip to main content

Errors in async

The main rule

Any async function always returns a promise. If it throws an error (throw) inside, the promise moves to the rejected state.

So errors can be handled in two ways:

  1. via try...catch inside the function itself;
  2. via .catch() outside, at the call site.

1. Handling via try...catch (inside the function)

This is the most readable and familiar way - it resembles synchronous code.

javascript
async function getData() { try { const res = await fetch('https://api.github.com/404'); // network error const data = await res.json(); console.log('Received:', data); } catch (error) { console.error('Error inside async:', error.message); } } getData();

Output:

javascript
Error inside async: Failed to fetch

try...catch "catches":

  • errors thrown via throw;
  • rejected promises that have await on them.

How it works

Under the hood, await does try { ... } catch { ... } for a promise that can end up with an error (reject).

javascript
const result = await Promise.reject('error'); // throws an exception → goes to the catch block

2. Handling via .catch() (outside the function)

Since an async function returns a Promise, you can handle the error with the regular .catch() method:

javascript
async function getUser() { const res = await fetch('https://wrong.url'); return res.json(); } getUser() .then(console.log) .catch(err => console.error('Error outside:', err.message));

Output:

javascript
Error outside: Failed to fetch

The difference from try...catch: here the error is handled at the call site, not inside the function.


3. Example with a manual throw

javascript
async function example() { const data = await Promise.resolve('OK'); if (!data) throw new Error('No data!'); return data; } example() .then(console.log) .catch(err => console.error(err.message));

Output:

javascript
OK

If the condition triggers, throw turns into Promise.reject().


4. Combining try...catch with a re-throw

Sometimes you need to handle an error partially, but pass it further - for example, to log it.

javascript
async function processData() { try { const res = await fetch('https://wrong.url'); return await res.json(); } catch (err) { console.error('Request error:', err.message); throw new Error('Error while processing data'); } } processData().catch(err => console.error('Final error:', err.message));

Output:

javascript
Request error: Failed to fetch Final error: Error while processing data

5. Handling several errors in a row

You can use several try...catch blocks for different stages:

javascript
async function pipeline() { try { const res = await fetch('/api/data'); try { const data = await res.json(); console.log('Data:', data); } catch (err) { console.error('JSON parsing error:', err.message); } } catch (err) { console.error('Request error:', err.message); } }

This approach is useful when you need to handle different levels of errors separately.


6. Handling without try...catch (via a utility)

Sometimes it's more convenient to use a helper function:

javascript
const to = (promise) => promise.then(data => [null, data]).catch(err => [err]); async function run() { const [err, data] = await to(fetch('/api/user').then(r => r.json())); if (err) return console.error('Error:', err.message); console.log(data); }

This pattern is often used in Node.js, so you don't have to write try...catch at every step.


7. A global catch for unhandled errors

If you forget .catch() or try...catch, you can catch the error globally:

javascript
window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise:', event.reason); });

This is the equivalent of try...catch for every promise that has no .catch().


A simple analogy

Think of an async function as a quest.

  • await is a "task" that can fail;
  • try...catch is "insurance": if it fails, you catch the error and decide what to do;
  • .catch() outside is the "game master" who catches fatal errors if the player did not manage.

SUMMARY

WayWhere it catches the errorExample
try...catchinside the async functiontry { await ... } catch (e) { ... }
.catch()at the function callmyAsync().catch(e => ...)
Global catchfor all unhandled oneswindow.addEventListener('unhandledrejection', ...)
The [err, data] patternvia a wrapperconst [err, res] = await to(promise)

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.