Event Loop, Callbacks, Promises, и Async/Await, часть 4 из 4

В предыдущей части руководства хорошо видно, что обещания (promises) и использование then для обработки асинхронных действий легче для понимания, чем пирамида обратных вызовов (callback). ECMAScript 2016 (ES7) предлагает функцию async и ключевое слово await.

Асинхронные функции с async/await

Функция async позволяет обрабатывать асинхронный код таким образом, чтобы он выглядел синхронным. Функция async под капотом по-прежнему использует обещания (promise), но имеет более традиционный JavaScript-синтаксис. В этом разделе рассмотрим примеры этого синтаксиса.

Для создания функции async, достаточно добавить ключевое слово async перед функцией:

// Асинхронная функция
async function getUser() {
  return {}
}

Хотя эта функция ещё не обрабатывает ничего асинхронного, её поведение отличается от традиционной функции. Если вы выполните функцию, вы обнаружите, что она вместо значения возвращает обещание с [[PromiseStatus]] и [[PromiseValue]]. Попробуйте залогировать вызов функции getUser:

console.log(getUser())

Вернётся следующее:

__proto__: Promise
[[PromiseStatus]]: "fulfilled"
[[PromiseValue]]: Object

Это означает, что вы можете обрабатывать функцию async с помощью then так же, как и обещание (promise). Попробуйте это с помощью следующего кода:

getUser().then(response => console.log(response))

Такой вызов getUser передаёт возвращаемое значение анонимной функции, которая записывает значение в консоль. При выполнении этого кода получите следующее:

{}

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

Запрос fetch из предыдущего раздела можно переписать следующим образом с помощью async/await:

// Обработка fetch с помощью async/await
async function getUser() {
  const response = await fetch('https://api.github.com/users/octocat')
  const data = await response.json()

  console.log(data)
}

// запуск
getUser()

Здесь оператор await гарантирует, что данные не попадут в консоль раньше, чем их вернёт асинхронный запрос.

Теперь data можно обрабатывать внутри функции getUser без использования then. Результат, который попадёт в консоль:

login: "octocat",
id: 583231,
avatar_url: "https://avatars3.githubusercontent.com/u/583231?v=4"
blog: "https://github.blog"
company: "@github"
followers: 3203
...

Примечание. Во многих средах режим async необходим для использования await, однако некоторые новые версии браузеров и Node разрешают использовать await верхнего уровня, что позволяет обойтись без создания функции async для завершения ожидания.

Наконец, поскольку в асинхронной функции обрабатывается выполненное обещание (promise), можно легко обрабатывать ошибку внутри этой функции. В этом случае вместо использования методов catch и then для обработки исключения нужно использовать шаблон try/catch. Вот как это выглядит:

// Обработка успеха и ошибок с помощью async/await
async function getUser() {
  try {
    // Успешно в try
    const response = await fetch('https://api.github.com/users/octocat')
    const data = await response.json()

    console.log(data)
  } catch (error) {
    // Ошибка в catch
    console.error(error)
  }
}

Теперь, если вернётся ошибка, будет выполнен код из блока catch, который запишет эту ошибку в консоль.

Современный асинхронный JavaScript-код легко обрабатывается с помощью синтаксиса async/await, но важно иметь практические знания о том, как работают обещания (promise), потому что у них есть дополнительные функции, которые нельзя обработать с помощью async/await. Например, объединение обещаний с помощью Promise.all().

Итог

Поскольку веб-API часто предоставляют данные асинхронно, изучение того, как обрабатывать результат асинхронных действий, является важной частью работы JavaScript-разработчика. В этой серии статей было рассмотрено, как используется цикл событий для обработки порядка выполнения кода со стеком и очередью. Были рассмотрены три способа обработки успеха или отказа асинхронного события с помощью обратных вызовов (callback), обещаний (promise) и синтаксиса async/await. Для обработки асинхронных действий использовался Fetch Web API.