async/await
async/await
Технический фундамент async-функций
async-функция всегда возвращает Promise, даже если внутри возвращается обычное значение. Это означает:
return valueвнутриasyncэквивалентенPromise.resolve(value);throw errorвнутриasyncэквивалентенPromise.reject(error);awaitтолько "раскрывает" Promise в локальной точке функции.
Понимание этого соответствия упрощает переход между then/catch и async/await без магии.
Почему async/await так любят в прод-коде
Promise-цепочки уже решают проблему callback hell, но при длинных сценариях они все равно читаются тяжелее, чем линейный код. async/await дает синтаксис, который выглядит почти как синхронный, но работает асинхронно.
Ключевой момент: async/await это синтаксический слой над Promise, а не отдельный механизм выполнения.
Проверь себя: почему функция с async всегда возвращает Promise?
Базовый синтаксис
asyncставится перед функцией.awaitможно использовать только внутриasync-функции.await"ждет" завершенияPromiseи возвращает его результат.
function loadUser() {
return Promise.resolve({ id: 1, name: 'Саша' });
}
async function showUser() {
const user = await loadUser();
console.log(user.name);
}
showUser();
Смотри, что важно: await не блокирует весь JavaScript-движок, он приостанавливает только текущую async-функцию до готовности результата.
Обработка ошибок с try...catch
В async/await ошибки удобно ловить привычным try...catch.
async function loadProfile() {
try {
const response = await fetch('/api/profile');
const data = await response.json();
return data;
} catch (error) {
console.error('Не удалось загрузить профиль:', error.message);
return null;
}
}
Это читается как линейный сценарий: запрос -> парсинг -> возврат результата.
Здесь часто путаются: если забыть await response.json(), ты получишь Promise, а не готовый объект данных.
Проверь себя: какой тип значения будет у data без await?
Последовательное и параллельное ожидание
Иногда нужно дождаться операций по очереди, иногда выгоднее запускать их параллельно.
Последовательно:
const user = await loadUser();
const orders = await loadOrders(user.id);
Параллельно:
const [user, settings] = await Promise.all([loadUser(), loadSettings()]);
Ключевой момент: если операции независимы, параллельный запуск обычно быстрее.
Микро-сценарии из реального интерфейса
- Экран профиля.
awaitдля загрузки данных пользователя.- если ошибка - показываем fallback-состояние.
- Дашборд.
- параллельно подгружаем карточки метрик через
Promise.all. - после получения всех данных рендерим экран целиком.
Это дает более предсказуемый UX, чем случайный порядок отрисовки фрагментов.
Типичные ошибки новичков
- Использовать
awaitвнеasync-функции. - Забывать
try...catchвокруг потенциально падающих операций. - Писать лишнюю последовательность там, где можно параллельно.
- Забывать вернуть результат из
async-функции. - Использовать
awaitвнутриforEachи ожидать, что цикл "подождет".
// Плохо: forEach не умеет "ждать" async-callback
items.forEach(async (item) => {
await saveItem(item);
});
// Последовательно (если нужно строго по очереди)
for (const item of items) {
await saveItem(item);
}
// Параллельно (если операции независимы)
await Promise.all(items.map((item) => saveItem(item)));
async function getValue() {
await Promise.resolve(10);
}
getValue().then((v) => console.log(v)); // undefined
Анти-провал: если функция должна отдавать данные, возвращай их явно после await.
Что будет, если изменить входные данные
Если fetch('/api/profile') вернет ошибку сети, управление перейдет в catch, и функция вернет null. Если ответ корректный, вернется объект профиля. Такое поведение легко предсказать и протестировать.
Проверь себя: зачем в catch возвращать осмысленный fallback, а не просто молча глотать ошибку?
Дополнительный пример: эквивалент return и throw в async.
async function getNumber() {
return 42;
}
async function failTask() {
throw new Error('Task failed');
}
Краткий итог
async/awaitделает асинхронный код ближе к линейному стилю.awaitработает только внутриasyncи ожидаетPromise.- Ошибки в
async-коде удобно обрабатывать черезtry...catch. - Независимые операции лучше запускать параллельно через
Promise.all. - Явные возвраты и понятные fallback делают
async-логику устойчивой.