Определение из Документов MDN-

Генераторы - это функции, из которых можно выйти, а затем снова войти. Их контекст (привязки переменных) будут сохраняться при повторных входах.

Смущенный?

Хорошо, позвольте мне прояснить вам ситуацию. Вы знаете асинхронные функции? В асинхронных функциях мы ставим ожидание, чтобы приостановить нашу функцию при определенном событии (событие может быть любой другой асинхронной операцией или простым вызовом API). Функции генератора служат тому же самому. Они ведут себя так же, как асинхронные функции. Тогда в чем разница?

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

Хватит теории, давайте теперь рассмотрим несколько примеров функций генератора.

function* foo(x) {
   var y = x * (yield);
   return y;
}
var it = foo(6);
//starts foo
it.next();
var res = it.next(7)
console.log(res.value)  //42

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

  • Имя функции генератора должно начинаться с символа * или использовать «функцию *», чтобы отличать его от обычных функций.
  • Вызов функции генератора не выполняет ее тело немедленно, вместо этого возвращается объект итератор для функции.
  • Вызов объекта итератора с помощью next выполняет тело функции генератора до тех пор, пока не встретит yield. Как только он обнаруживает его, он приостанавливается и ждет следующего сообщения, чтобы инициировать его снова. Итак, что такое сообщение?
  • Генераторные функции своего рода работают над системой передачи сообщений. Вызов next () передает сообщение yield для его запуска, а затем next yield, когда он приостановлен, передает свой результат объекту итератора.
  • когда next вызывается без каких-либо аргументов, yield предполагает, что переданное сообщение не определено, и, следовательно, запускает функцию с последней точки выполнения.

Теперь давайте посмотрим шаг за шагом, что происходит в этом примере -

  • функция foo запускается со значением x как 6, и возвращается объект-итератор.
  • Чтобы начать выполнение foo, мы вызвали it.next () без аргументов. На этот раз мы не передали никаких аргументов, потому что это начальный вызов, просто чтобы запустить foo до первого yield. На данный момент никакой доходности не ждет ответа. Таким образом, нет необходимости передавать что-либо, если вы все же хотите что-то передать, это будет просто проигнорировано.
  • Теперь наша функция будет приостановлена ​​на yield и вернет полученное значение ожидающему вызову next (), следующий метод возвращает объект, подобный этому
 {
 value : “yielded value” ,
 done  : “Whether the function terminates"
 }
  • Итак, в первом следующем вызове он вернет значение undefined, поскольку значение не возвращается, и будет выполнено как false, потому что функция все еще должна быть выполнена, и мы еще не закончили.
  • Теперь мы снова вызываем объект-итератор, чтобы снова запустить функцию с последней точки выполнения, которая была первой yield, она ждала следующего «следующего вызова». Не запутайтесь, я сказал вам, что yield будет ждать следующего вызова, чтобы получить сообщение (или значение).
  • Предоставляя значение 7 для ожидаемого yield, мы переходим к этой точке, var y = x * (yield = 7)
var y = x * (yield=7)
var y = 6 * 7 = 42
  • Теперь функция выполняется до завершения, поскольку доходности не осталось, она обнаружит return и вернет значение обратно в ожидание следующего вызова.
  • Теперь, ожидая следующего звонка, мы получаем,
 {
   value : 42,
   done  : true
 }

и значение 42 будет напечатано на консоли.

Это все для этой статьи, подробнее мы поговорим в следующей статье :)