Как генератор работает с Promise, Async/Await в ES7

Чтобы полностью понять эту статью, рекомендуется хотя бы более-менее ознакомиться с генератором и Promise.

Фон

Асинхронный JavaScript быстро развивается и привлекает все больше внимания разработчиков интерфейсов. Во-первых, давайте вспомним, через что мы прошли за последние несколько лет.

Сначала у нас есть обратные вызовы AJAX, предоставляемые такими утилитами, как $ajax и т. д. Это интуитивно понятно и просто, поскольку сам Node.js также основан на шаблоне обратного вызова. Затем, из-за известного «ада обратных вызовов», мы придумали Promise в ES6, который позволяет нам связывать поток выполнения. , а не вложить его.

И в настоящее время мы начинаем больше слышать о таких ключевых словах, как async, await и генераторы. Это еще более продвинутый синтаксический сахар, который обеспечивает более удобный для разработчиков опыт работы с асинхронными проблемами. Давайте посмотрим на это сейчас.

Асинхронно и ждать

Ключевые слова async и await добавлены ES7. На первый взгляд, эти два ключевых слова делают асинхронный поток более похожим на синхронный. Эти два ключевых слова всегда идут в паре. В частности, «асинхронный» всегда используется перед объявлением функции и нигде больше. Это указывает на то, что следующая функция имеет какое-то отношение к асинхронным операциям. А выражение «ожидание» используется внутри «асинхронной» функции. Он приостанавливает поток выполнения и ждет возврата результата промиса.

Давайте посмотрим на пример:

Здесь мы видим, что функция resolveAfter2Seconds возвращает обещание, которое разрешает то, что принимается в качестве параметра, через 2 секунды. Это то, что мы уже знаем о Promise. Интересная часть заключается в «асинхронной» функции addTwoResult. Глядя на поток выполнения, ключевое слово «ожидание» делает асинхронную операцию похожей на обычный синхронный код, который мы пишем. Но под капотом он не продолжает выполняться, пока не завершит текущую асинхронную операцию.

Другими словами, поток выполнения выглядит следующим образом. Сначала запустите resolveAfter2Seconds(a), сделайте паузу на 2 секунды и присвойте его значение «first». Затем запустите resolveAfter2Seconds(b), сделайте паузу еще на 2 секунды и присвойте его значение «секунде». И, наконец, верните сумму первого и второго.

Как это легко и прямолинейно! Но вам может быть интересно, что происходит под капотом.

Генератор

Оказывается, пара ключевых слов «async» и «await» — это просто синтаксический сахар функции-генератора, именно благодаря этому это происходит в реальном мире.

Во-первых, давайте кратко напомним, для чего используется генератор.

Генератор — это объект, возвращаемый функцией-генератором. Генератор соответствует протоколу iterable и iterator, то есть у него есть метод next, который возвращает объект с двумя свойствами: value и done. Давайте рассмотрим очень простой пример:

Обратите внимание, как объявлена ​​функция генератора: звездочка (*) используется перед именем функции, чтобы уведомить, что она возвращает генератор. И самая важная часть функции-генератора заключается в том, что она приостанавливает выполнение каждый раз, когда вы что-то выдаете, пока вы явно не вызовете next для возвращенного генератора, то есть она не вы даже не знаете, что произойдет дальше, пока не вызовете «gen.next()» в строке 9 и не вернете 3.

Это особенно полезно, когда вы выполняете дорогостоящие вычисления внутри функции. Более наглядный пример для этого сценария приведен ниже:

Генераторная функция может не только что-то выдать, но и что-то вернуть, что заранее завершает функцию, как это происходит в обычных функциях.

Еще одна вещь, о которой стоит упомянуть, это то, что вы также можете передать что-то в функцию next в качестве параметра, и значение, которое вы передали, станет возвращаемым значением вашего последнего выражения yield, которое приостановило генератор. Эта функция позволяет нам возвращать значение асинхронных операций.

Зная это, мы могли бы вернуться к асинхронному режиму и ждать. На самом деле асинхронность — это просто звездочка в функции-генераторе. А await — это именно ключевое слово «доходность», как показано в следующем коде:

Но как? Я все еще чувствую, что чего-то не хватает, и хочу узнать больше.

Наша собственная асинхронная оболочка

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

Давайте сначала подумаем над несколькими вопросами после прочтения приведенного выше фрагмента кода:

  1. В Promise мы получаем результат, связывая метод then, но как мы получаем результат, возвращая его и присваивая его переменной?
  2. Как мы представляем обещание, которое было отклонено при уступке?

Давайте посмотрим на реализацию ниже:

Есть несколько вещей, которые необходимо объяснить:

  1. Сначала мы вызываем next() без параметров, чтобы получить первое обещание. И после этого мы связываем промис, чтобы получить ответ, и передаем каждый из них в generator.next, чтобы мы могли сигнализировать, что промис был выполнен этим ответом.
  2. Поскольку мы используем блок try-catch в основном потоке, каждый раз, когда мы связываем промис, и если мы получаем отказ, он перехватывает ошибку и останавливает выполнение.
  3. При вызове generate.next()возвращается {done: true}, что означает, что все выполнение функции асинхронного генератора выполнено. Мы разрешаем значение и возвращаемся. (Обратите внимание, что в то время значение, вероятно, не определено)

Хорошо, в общем, мы говорили об async/await в ES7, который семантически более прямолинеен, чем Promise. А затем мы подытожили, как генератор появился в нашей жизни, как он лениво вычисляется и, таким образом, полезен для дорогостоящих вычислений. Наконец-то мы соединяем генераторы и обещаем сами написать асинхронную обертку! Ура!

Дальнейшее чтение

Генераторы

Co — непревзойденное качество управления потоком на основе генератора для Nodejs (поддерживает переходы, промисы и т. д.)

Bluebird — полнофункциональная библиотека промисов с непревзойденной производительностью.