How to handle errors in async code
Short answer
With await, errors are caught by an ordinary try...catch:
const loadUser = async () => {
throw new Error('User not found');
};
try {
await loadUser();
} catch (error) {
console.log('Error:', error.message);
}Output:
Error: User not foundThe catch method
Without await, attach .catch() to the promise:
const loadUser = async () => {
throw new Error('User not found');
};
await loadUser()
.then((user) => console.log(user))
.catch((error) => console.log('Error:', error.message));Output:
Error: User not foundA default value
.catch() can return a replacement value, and the code goes on:
const loadSettings = async () => {
throw new Error('offline');
};
const settings = await loadSettings().catch(() => ({ theme: 'light' }));
console.log(settings.theme);Output:
lightfinally
finally runs in any case: convenient for hiding a loading indicator:
let loading = true;
try {
await Promise.reject(new Error('failed'));
} catch (error) {
console.log(error.message);
} finally {
loading = false;
}
console.log('loading:', loading);Output:
failed
loading: falseCommon mistake
try...catch around a promise without await does not catch anything: the error happens later, when the block has already ended:
const fail = async () => {
throw new Error('late error');
};
try {
fail().catch((error) => console.log('caught only by .catch():', error.message));
} catch {
console.log('never runs');
}Output:
caught only by .catch(): late errorFunctions used in this recipe
- try...catch — Catches errors so the program does not stop.
- Promise.allSettled — Waits for every promise to settle and reports each result — success or failure.
- async function — A function that always returns a promise and lets you use await inside.