Как работает Async/Await в JavaScript

Hydrock
Front Stories
Published in
3 min readMay 29, 2018

На написание этого поста натолкнула статья How JavaScript Async/Await Works от Thon Ly

Введение

Используя ту или иную технологию, хочется глубже понять как она работает. Руки дошли и до Async/Await. Одним из лучших вариантов, разобраться как это работает, написать собственный полифил. Я продемонстрирую его ближе к концу поста, а пока давайте приведем пример.

Необходимо сделать запрос за некими данными, и после ответа вывести данные в консоль.

Для запроса будем использовать Fetch API — интерфейс для получения ресурсов (в том числе по сети). Запрос с помощью fetch возвращает Promise (обещание — которое будет выполнено или отклонено). Для того чтобы получить данные из промиса, нам нужно включить в цепочку два колбека с помощью метода then.

  1. Делаем запрос, получаем промис.
  2. После того как промис выполнен успешно, получаем специальный объект ответа. Выполним метод объекта ответа json(), чтобы получить данные.
  3. Метод json() возвращает промис, так что далее снова вызываем then и в колбеке выводим данные в консоль.

Все просто 😅

Используя Async/Await можем сделать так:

Мы обернули наш вызов в асинхронную функцию (async), заменили колбеки (then) на заявление await. Теперь код останавливает выполнение на ключевых словах await, до выполнения обещания, и продолжает выполнение далее. Асинхронное программирование как синхронное!

Круто, верно? 😉

На самом же деле Async/Await это просто синтаксический сахар. Я попытаюсь показать, как это работает внутри и для этого мы напишем асинхронную функцию с нуля.

Асинхронная функция

Ключевым моментом в асинхронной функции является использование генераторов (Generators). Давайте напишем функцию-генератор.

Функция генератор сама по себе не выполняется, она лишь создает специальный итерируемый объект знающий о своем состоянии. Этот объект имеет метод next() который позволяет запускать выполнение функции до ключевого слова yield. Инструкция yield, на мой взгляд, похожа на return, возвращает промежуточный результат и приостанавливает выполнение кода.

Но в таком виде, это просто функция генератор. Нам необходимо как то перебрать ее. Для этого напишем свою асинхронную функцию.

И тут распишу подробно.

  1. Назовем функцию async чтобы было похоже на нативную
  2. Создаем сам объект генератор
  3. Создадим функцию next, как и нативный метод. С ее помощью будем перебирать наш генератор. Функция принимает value — тут будем передавать промис полученный на этапе перебора генератора. В первом вызове (строчка 10) ничего не передается.
  4. В nextResult получаем результат первой итерации генератора. И как вы можете знать, нативный метод next генератора возвращает объект со значением value и done. В value находится промис запроса поста, а в done — булевое значение false, так как генератор еще не закончил свою работу и в его теле еще есть yield.
  5. Далее проверяем закончено ли выполнение генератора, и если да, просто возвращаем значение. В нашем случае это не так.
  6. Так как мы получили промис, выполним метод then, и передадим колбеком нами созданную функцию next.
  7. Далее выполняется следующий кусок кода генератора. И заметьте — теперь функция next получает value — промис из предыдущего вызова. Мы как бы заменяем yield fetch(‘https://jsonplaceholder.typicode.com/posts/1'); на наш промис и продолжаем выполнение.
  8. Натыкаемся на следующий yield. Метод json() обекта ответа так же возвращает промис. Так что вся процедура повторится еще один раз.
  9. И в последний раз функция next получает уже готовые данные. Перебор заканчивается, выходим из функции.

Фуххх 😮 Но если вглядеться — все довольно ожидаемо.

Результат смотрите на codepen, в консоли.

Заключение

Async/Await действительно позволяет писать асинхронный код синхронно, не блокируя стек вызовов. Мы замораживаем код и ждем пока выполнится промис, а затем продолжаем. Но внутри все работает именно на генераторах. Особенность асинхронных функций в том что они позволяют “вытащить” значение завершенного промиса без метода then.

Я надеюсь, вам понравился пост. Если да, похлопайте 👏, чтобы помочь другим найти эту информацию. И не стесняйтесь оставлять комментарий — буду рад!

--

--