async/await и обработка ошибок
async/await — синтаксис, который превращает асинхронный код в линейный, и как ловить в нём ошибки.
async/await — синтаксический сахар над промисами:
awaitприостанавливает функцию до выполнения промиса, не блокируя поток.
Основы
Функция с ключевым словом async всегда возвращает промис. Внутри неё await «разворачивает» промис: ждёт его значения и возвращает результат. Код читается сверху вниз, как синхронный, хотя выполняется асинхронно:
function delay(value, ms) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
async function main() {
console.log("начало");
const a = await delay("первый", 10);
console.log(a);
const b = await delay("второй", 10);
console.log(b);
console.log("конец");
}
main();Вывод:
начало первый второй конец
Обработка ошибок: обычный try/catch
Главное удобство await — ошибки ловятся привычным try/catch/finally, как в синхронном коде. Отклонённый промис превращается в исключение:
async function risky() {
throw new Error("сбой в async");
}
async function main() {
try {
await risky();
} catch (e) {
console.log("поймали:", e.message);
} finally {
console.log("очистка выполнена");
}
}
main();Вывод:
поймали: сбой в async очистка выполнена
Ошибки в цикле: изолируем каждую итерацию
Частая задача — обработать список, где отдельные элементы могут падать, но это не должно ронять весь процесс. Оборачиваем await в try/catch внутри цикла:
async function fetchUser(id) {
if (id < 0) throw new Error("неверный id");
return { id, name: "User" + id };
}
async function main() {
const ids = [1, -2, 3];
for (const id of ids) {
try {
const u = await fetchUser(id);
console.log("получен:", u.name);
} catch (e) {
console.log("ошибка для id=" + id + ":", e.message);
}
}
}
main();Вывод:
получен: User1 ошибка для id=-2: неверный id получен: User3
Главная ловушка: последовательный await вместо параллельного
Если задачи независимы, не ждите их по очереди — запустите параллельно. Сравните: await a; await b; ждёт суммарно, а Promise.all([a, b]) — пока завершится самая долгая. В примере оба delay стартуют до await, поэтому идут параллельно:
function delay(value, ms) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
async function main() {
const slow = delay("медленный (60мс)", 60);
const fast = delay("быстрый (10мс)", 10);
console.log("оба запущены, ждём...");
const results = await Promise.all([slow, fast]);
console.log("готово:", results.join(" | "));
}
main();Вывод:
оба запущены, ждём... готово: медленный (60мс) | быстрый (10мс)
Итог
async-функция всегда возвращает промис;awaitждёт значение, не блокируя поток.- Ошибки ловятся обычным
try/catch. - Независимые задачи запускайте через
Promise.all, а не цепочкойawait.