async/await: упрощение работы с асинхронным кодом
Что было до async/await?
До появления async/await разработчики использовали Promise и цепочки .then()/.catch() для работы с асинхронным кодом. Это работало, но код быстро превращался в «лапшу»:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data);
return fetch('https://api.example.com/more-data');
})
.then(anotherResponse => anotherResponse.json())
.catch(error => console.error('Ошибка:', error));
Такой подход сложно читать, особенно когда цепочка разрастается. async/await решает эту проблему, делая код линейным и понятным.
Как работает async/await?
async/await — это синтаксический сахар над Promise. Он позволяет писать асинхронный код так, будто он синхронный, но без блокировки основного потока.
asyncставится перед функцией и говорит: «Эта функция возвращает Promise».awaitставится перед Promise и говорит: «Дождись выполнения этого Promise».
Пример переписки кода выше:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
const anotherResponse = await fetch('https://api.example.com/more-data');
const moreData = await anotherResponse.json();
console.log(moreData);
} catch (error) {
console.error('Ошибка:', error);
}
}
fetchData();
Теперь код читается сверху вниз, как будто это обычные синхронные операции!
🔥 Важно:
awaitможно использовать только внутриasync-функций. В обычных функциях или глобальной области это вызовет синтаксическую ошибку.
Обработка ошибок
С async/await ошибки можно ловить с помощью try/catch, как в синхронном коде:
async function getUser(id) {
try {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) throw new Error('Ошибка сети');
const user = await response.json();
return user;
} catch (error) {
console.error('Не удалось загрузить пользователя:', error);
return null;
}
}
Альтернативный вариант — использовать .catch() при вызове функции:
getUser(123).catch(error => console.error('Ошибка:', error));
Параллельное выполнение
Если вам нужно выполнить несколько асинхронных операций параллельно, а не последовательно (как в примере выше), используйте Promise.all:
async function fetchMultipleUrls() {
const [users, posts, comments] = await Promise.all([
fetch('/users').then(res => res.json()),
fetch('/posts').then(res => res.json()),
fetch('/comments').then(res => res.json())
]);
console.log({ users, posts, comments });
}
Этот код загрузит все три ресурса одновременно, а не один за другим.
Когда использовать async/await?
async/await идеально подходит для:
- Последовательных асинхронных операций (например, загрузка данных A → обработка → загрузка данных B).
- Сложных цепочек с множеством
.then(). - Кода, где важна читаемость.
Однако, иногда Promise с .then() может быть лучше:
- Для простых цепочек из 1-2 шагов.
- Когда нужен точный контроль над потоком выполнения.
Под капотом: как это работает?
async/await использует генераторы и Promise под капотом. Когда движок JavaScript видит await, он:
- Приостанавливает выполнение функции.
- Ждёт разрешения Promise.
- Возобновляет выполнение функции с результатом.
Это похоже на «приостановку» потока, но без реальной блокировки, потому что JavaScript однопоточный!
Практический пример: загрузка данных
Давайте создадим реалистичный пример — загрузку пользователя и его постов с JSONPlaceholder API:
async function loadUserAndPosts(userId) {
try {
// Загружаем пользователя
const userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!userResponse.ok) throw new Error('Пользователь не найден');
const user = await userResponse.json();
// Затем загружаем его посты
const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('Ошибка загрузки:', error);
throw error; // Пробрасываем ошибку дальше
}
}
// Использование
loadUserAndPosts(1)
.then(data => console.log('Данные:', data))
.catch(() => console.log('Не удалось загрузить данные'));
Итоги
async/awaitделает асинхронный код чище и понятнее.- Ошибки ловятся через
try/catch— привычный синхронный стиль. - Для параллельного выполнения используйте
Promise.all. - Помните:
awaitработает только внутриasync-функций.
Теперь ваш асинхронный код будет таким же чистым и понятным, как синхронный! 🎉