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, он:

  1. Приостанавливает выполнение функции.
  2. Ждёт разрешения Promise.
  3. Возобновляет выполнение функции с результатом.

Это похоже на «приостановку» потока, но без реальной блокировки, потому что 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-функций.

Теперь ваш асинхронный код будет таким же чистым и понятным, как синхронный! 🎉

Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty