Что такое async/await в JavaScript?
Что такое async/await
async/await — это синтаксическая конструкция, появившаяся в ECMAScript 2017 (ES8), которая упрощает работу с промисами. По сути это «обёртка» над Promise API, позволяющая писать асинхронный код так, будто он выполняется последовательно и синхронно. При этом цикл событий не блокируется, а сам код становится гораздо читабельнее, чем цепочки .then().
Ключевое слово async
Ключевое слово async ставится перед объявлением функции и делает её асинхронной. Такая функция всегда возвращает Promise, даже если внутри явно вернуть обычное значение — оно будет автоматически обёрнуто в Promise.resolve(). Если внутри async-функции выбрасывается исключение, возвращённый промис переходит в состояние rejected.
async function getNumber() {
return 42; // вернётся Promise<42>
}
getNumber().then(value => console.log(value)); // 42
Ключевое слово await
Ключевое слово await можно использовать только внутри async-функций (или на верхнем уровне ES-модулей — top-level await). Оно «приостанавливает» выполнение функции до тех пор, пока промис не будет разрешён, и возвращает его результат. Если промис отклоняется, await пробрасывает ошибку, которую можно поймать через try/catch.
async function loadUser(id) {
// приостанавливаем выполнение до получения ответа
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
Обработка ошибок
Для обработки ошибок в async/await используется привычный механизм try/catch, что делает код единообразным с синхронным. Это одно из главных преимуществ перед .catch() у промисов.
async function safeLoad() {
try {
const data = await fetch('/api/data');
return await data.json();
} catch (error) {
// ловим и сетевые ошибки, и ошибки парсинга JSON
console.error('Ошибка загрузки:', error);
return null;
}
}
Параллельное выполнение
Последовательные await-вызовы выполняются один за другим, что может замедлить код. Для параллельного запуска независимых операций используйте Promise.all совместно с await.
// плохо: запросы идут последовательно (~2 секунды)
const user = await fetchUser();
const posts = await fetchPosts();
// хорошо: запросы идут параллельно (~1 секунда)
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts(),
]);
Связь с промисами
Важно понимать, что async/await не заменяет промисы, а работает поверх них. Любую async-функцию можно переписать через .then(), и наоборот. Под капотом движок превращает async-функцию в конечный автомат, который продолжает работу при разрешении промиса в микрозадаче.
Что хочет услышать интервьюер
Понимание, что async-функция всегда возвращает Promise
Знание, что await работает только внутри async-функций (или на top-level в модулях)
Умение обрабатывать ошибки через try/catch
Понимание разницы между последовательным и параллельным выполнением (Promise.all)
Осознание, что async/await — это синтаксический сахар над промисами, а не замена им
Пример: Базовый пример async/await с обработкой ошибок
interface User {
id: number;
name: string;
}
async function fetchUser(id: number): Promise<User | null> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
// ждём парсинга JSON
const user: User = await response.json();
return user;
} catch (error) {
console.error('Не удалось загрузить пользователя:', error);
return null;
}
}
// использование
const user = await fetchUser(1);
Пример: Параллельное выполнение через Promise.all
async function loadDashboard(userId: number) {
// все три запроса стартуют одновременно
const [user, posts, comments] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/users/${userId}/posts`).then(r => r.json()),
fetch(`/api/users/${userId}/comments`).then(r => r.json()),
]);
return { user, posts, comments };
}
Типичные ошибки
Использование await вне async-функции, что приводит к синтаксической ошибке
Последовательные await для независимых операций вместо Promise.all — потеря производительности
Забывают про try/catch и теряют ошибки в виде unhandled promise rejection
Думают, что async/await делает код синхронным и блокирует поток
Использование await в forEach — он не дожидается завершения промисов; нужен for...of или Promise.all с map


