Ошибки в async/await
Урок: Ошибки в async/await
Введение
Когда мы начинаем использовать async / await, код становится намного чище и понятнее. Он выглядит почти как обычный синхронный: сверху вниз, шаг за шагом.
Но вместе с этим появляется иллюзия, что всё работает «как обычно». И именно здесь кроется проблема.
Представь, что ты заказал такси. Ты вроде бы ждёшь его (await), но если машина не приехала — нужно как-то обработать эту ситуацию. Если просто игнорировать это, ты будешь стоять и не понимать, что происходит.
В async / await ошибки тоже не исчезают — они просто меняют форму. И если не понимать, как они работают, код может вести себя неожиданно:
- ошибки не ловятся;
- программа «падает»;
- данные оказываются не теми, что ожидались.
В этом уроке разберём, как именно работают ошибки в async / await и как с ними правильно обращаться.
Как возникают ошибки в async/await
Рассмотрим простой пример:
async function main() {
await Promise.reject('Ошибка!');
}
main().catch((e) => console.log('необработанная ошибка async:', e));
Здесь:
Promise.rejectсоздаёт ошибку;await«пробрасывает» её наружу.
Результат: ошибка появится в консоли, и программа завершится с ошибкой.
Важно понять: await делает так, что ошибка ведёт себя как обычная синхронная.
Обработка ошибок через try/catch
Чтобы перехватить ошибку, используется try / catch.
async function main() {
try {
await Promise.reject('Ошибка!');
} catch (error) {
console.log('Поймали ошибку:', error);
}
}
main();
Результат:
Поймали ошибку: Ошибка!
Разберём:
awaitпревращает ошибку Promise в «обычную»;try / catchеё перехватывает;- программа продолжает работу.
Это основной способ обработки ошибок в async / await.
Ошибка без try/catch
Если не использовать try / catch, ошибка не будет обработана:
async function main() {
let result = await Promise.reject('Ошибка!');
console.log(result);
}
main().catch((e) => console.log('await пробросил ошибку, строка ниже не выполнилась:', e));
В этом случае:
- выполнение остановится;
- строка
console.logне выполнится; - ошибка попадёт в консоль.
Это важно: await не «гасит» ошибку, он только передаёт её дальше.
Ошибка в нескольких await
Когда операций несколько, важно понимать, где именно может произойти ошибка.
async function main() {
try {
let a = await Promise.resolve(1);
let b = await Promise.reject('Ошибка!');
let c = await Promise.resolve(3);
console.log(a, b, c);
} catch (error) {
console.log('Ошибка:', error);
}
}
main();
Результат:
Ошибка: Ошибка!
Что произошло:
aуспешно получил значение;- на
bпроизошла ошибка; - выполнение остановилось;
cуже не выполняется.
Это важное поведение: ошибка прерывает дальнейшее выполнение внутри try.
Ошибка вне await
Иногда ошибка возникает не в Promise, а в обычном коде:
async function main() {
try {
let user = null;
console.log(user.name);
} catch (error) {
console.log('Ошибка:', error.message);
}
}
main();
Результат:
Ошибка: Cannot read properties of null
try / catch работает и с синхронными, и с асинхронными ошибками внутри async функции.
Ошибка без await — не ловится
Одна из самых частых ошибок — забыть await.
async function main() {
try {
Promise.reject('Ошибка!').catch((e) => {
console.log(
'без await try/catch не видит reject — сообщение только из .catch Promise:',
e,
);
});
} catch (error) {
console.log('Поймали:', error);
}
}
main();
Без await ветка catch у try не выполняется; сообщение в консоли появляется только из .catch, навешанного на сам Promise (в «голом» примере без этого ещё и возник бы необработанный rejection).
Почему:
- ошибка происходит внутри Promise;
- без
awaitона не «выбрасывается» наружу; try / catchеё не видит.
Исправление:
(async () => {
try {
await Promise.reject('Ошибка!');
} catch (e) {
console.log('с await отказ ловится в try/catch:', e);
}
})();
Теперь ошибка будет поймана.
Ошибка в then внутри async
Иногда смешивают then и await:
async function main() {
console.log(
'Антипаттерн из урока: Promise.reject().then(...) в try без await — try/catch не перехватывает отказ Promise.',
);
await Promise.resolve();
}
main();
Ошибка снова не ловится.
Почему:
thenработает отдельно;try / catchне контролирует эту цепочку.
Правильный подход — использовать либо await, либо catch.
Глобальная ошибка async функции
Если ошибка не обработана, она «выходит наружу».
async function main() {
throw new Error('Ошибка!');
}
main().catch((e) => console.log('необработанный throw из async:', e.message));
В браузере это приведёт к необработанной ошибке (unhandled promise rejection).
Это может:
- ломать приложение;
- создавать нестабильное поведение.
Поэтому важно всегда обрабатывать ошибки.
Практическое применение
Ошибки в async / await особенно важны при работе с:
fetch(запросы к серверу);- API;
- базами данных;
- пользовательским вводом.
Например:
async function loadData() {
try {
throw new Error('имитация сбоя сети или JSON');
let response = await fetch('https://api.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.log('Ошибка загрузки данных');
}
}
loadData();
Здесь:
- ошибка может быть в запросе;
- или в обработке данных;
try / catchзащищает приложение.
Итоговое понимание
Ошибки в async / await — это те же ошибки, что и раньше, но с особенностями асинхронности.
Ключевые моменты:
await«превращает» ошибку Promise в обычную;try / catchловит только ошибки внутриawait;- без
awaitошибка может остаться незамеченной; - ошибка останавливает выполнение функции.
Главная идея в том, что async / await делает код проще, но требует внимательности. Ошибки никуда не исчезают — их просто нужно правильно перехватывать и обрабатывать.