async / await

Урок: async / await

Введение

Когда мы разобрались с Promise, стало понятно, что они помогают работать с асинхронным кодом. Но при этом у них есть проблема: цепочки then могут становиться длинными и сложными для чтения.

Представь, что тебе нужно выполнить несколько действий подряд: сначала загрузить пользователя, потом получить его заказы, потом обработать их. С then это превращается в цепочку, где легко запутаться.

В реальной жизни мы думаем по-другому: «сначала сделай это, потом это, потом это». То есть последовательно.

Именно такую возможность даёт async / await.

Он позволяет писать асинхронный код так, как будто он синхронный — проще, понятнее и ближе к обычному мышлению.


Что такое async

Ключевое слово async используется для объявления асинхронной функции.

Чтобы увидеть результат в консоли, объявление и потребление Promise объединим в один фрагмент:

async function sayHello() {
  return 'Привет';
}

sayHello().then((result) => {
  console.log(result);
});

Результат:

Привет

Даже если ты просто возвращаешь строку, JavaScript автоматически «оборачивает» её в Promise.

Это значит: async превращает функцию в асинхронную.


Что такое await

Ключевое слово await используется внутри async функции.

Оно «останавливает» выполнение функции до тех пор, пока Promise не завершится.

Пример:

function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Данные получены');
    }, 1000);
  });
}

async function main() {
  let result = await getData();
  console.log(result);
}

main();

Результат через ~1 секунду:

Данные получены

Разберём:

  • getData() возвращает Promise;
  • await ждёт его выполнения;
  • результат записывается в result;
  • дальше код продолжается.

Как это выглядит по сравнению с then

Посмотрим два варианта на мгновенно выполняющемся Promise (без долгой задержки), чтобы в песочнице быстрее увидеть вывод.

Через then:

function getData() {
  return Promise.resolve('Данные получены');
}

getData().then((result) => {
  console.log(result);
});

Через async/await:

function getData() {
  return Promise.resolve('Данные получены');
}

async function main() {
  let result = await getData();
  console.log(result);
}

main();

Второй вариант читается проще — как обычный последовательный код.


Несколько асинхронных операций подряд

Одна из главных причин использовать async / await — это работа с несколькими операциями.

function step1() {
  return Promise.resolve('Шаг 1');
}

function step2() {
  return Promise.resolve('Шаг 2');
}

async function process() {
  let result1 = await step1();
  console.log(result1);

  let result2 = await step2();
  console.log(result2);
}

process();

Результат:

Шаг 1
Шаг 2

Здесь код читается сверху вниз, как обычная последовательность действий.


Обработка ошибок через try/catch

С Promise ошибки обрабатываются через catch. С async / await используется обычный try/catch.

function getData() {
  return new Promise((resolve, reject) => {
    reject('Ошибка!');
  });
}

async function main() {
  try {
    let result = await getData();
    console.log(result);
  } catch (error) {
    console.log(error);
  }
}

main();

Результат:

Ошибка!

Это делает код более привычным, особенно если ты уже знаком с синхронными ошибками.


Важные ограничения

await можно использовать только внутри async функций (или в модулях с поддержкой top-level await).

Нельзя написать «голый» await в обычном скрипте вне async — будет синтаксическая ошибка. Правильный шаблон — обернуть ожидание в async-функцию и вызвать её:

function getData() {
  return Promise.resolve('ok');
}

async function main() {
  let result = await getData();
  console.log(result);
}

main();

Это одно из ключевых правил.


Что происходит на самом деле

Хотя await выглядит как «ожидание», JavaScript не блокирует выполнение всей программы.

Он:

  • «ставит на паузу» только текущую функцию;
  • остальной код продолжает выполняться;
  • когда Promise завершается — функция продолжает работу.

Это важно: await не делает код синхронным, он просто делает его более читаемым.


Где это используется на практике

async / await используется почти везде:

  • запросы к серверу (fetch);
  • работа с API;
  • загрузка данных;
  • обработка форм;
  • взаимодействие с базами данных.

Например, два последовательных «шага» без реальной сети (имитация fetch + json()):

async function loadUser() {
  let response = await Promise.resolve({
    json: async () => ({ name: 'Stepan', role: 'student' }),
  });

  let data = await response.json();

  console.log(data);
}

loadUser();

Здесь:

  • сначала ждём «ответ» (здесь это уже готовый объект);
  • затем ждём json() (тоже Promise);
  • затем используем данные.

В реальном коде вместо Promise.resolve({...}) будет await fetch(url).


Итоговое понимание

async / await — это способ работать с асинхронным кодом так, как будто он синхронный.

async делает функцию асинхронной и возвращающей Promise. await позволяет дождаться результата и работать с ним как с обычным значением.

Ключевая идея в том, что async / await упрощает чтение и написание кода. Он не меняет саму асинхронность, а делает её более понятной.

Если Promise — это инструмент, то async / await — это удобный способ им пользоваться.