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

Генераторы возвращают значение с помощью ключевого слова yield. Когда это происходит, генератор приостанавливает свое выполнение без блокировки, возвращает значение и терпеливо ждет, пока не будет получен другой запрос.

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

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

Вот наш простой интерфейс. Я использовал Bootstrap, чтобы он выглядел презентабельно.

HTML.

И код, который заставляет это работать.

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

Первое, на что следует обратить внимание, это то, что мы используем IIFE (Immediately Invoked Function Expression), чтобы при загрузке страницы у нас автоматически была доступна переменная testGen, свисающая с объекта окна. Мы можем подтвердить это с помощью инструментов разработчика Chrome, открыв вкладку консоли и введя window.testGen. Он производит следующий вывод:

Инструменты разработчика подтверждают, что window.testGen является объектом и предоставляет функцию nextVal (). Также обратите внимание, что с помощью IIFE мы создали закрытие, которое указано в разделе Scopes в инструментах разработчика.

Поскольку testGen доступен для объекта окна, мы используем его, чтобы установить действие onclick для кнопки в нашей простой форме.

При нажатии кнопки выполняется nextVal ().

Сначала мы проверяем, истинна ли частная переменная _myIterator. Если это не так, мы вызываем 2 функции для настройки нашей среды выполнения: getStartingValue () и createIterator ().

В строке 8 вызывается функция myGen (), и возвращаемое значение сохраняется в переменной _myIterator. Что, черт возьми, такое _myIterator и зачем он нам? Я думал, этот пост о генераторах?

Давайте сделаем паузу и обсудим генераторы более подробно.

Генераторы и итераторы

Генераторы - это совершенно новый тип функций. Как указано выше, функция-генератор - это функция, которая создает последовательность значений, но не создает все значения сразу, как это сделала бы обычная функция. Мы должны явно запросить значение, и генератор вернет значение, если оно есть. Каждое возвращаемое значение представляет собой новый объект, который содержит метод next () для извлечения следующего значения в последовательности и свойство done, чтобы сообщить потребителю, есть ли какие-либо дополнительные значения. Если генератор не имеет дополнительных значений, он сообщит нам об этом, вернув значение undefined и свойство done установлено в значение true.

Каждый раз, когда мы запрашиваем значение, генератор «запоминает», где мы были во время последнего запроса, и продолжает с того места, где он остановился в прошлый раз, когда мы запрашивали значение.

Когда мы вызываем генератор, это не означает, что тело функции генератора будет выполнено. Вместо этого создается и возвращается объект Iterator. Мы, как потребитель, используем этот вновь созданный итератор как «посредника» между нами и генератором. Вот что происходит в строке 8 примера кода. Вызов myGen () возвращает итератор, который мы используем для связи с генератором.

Внимательные читатели могли заметить, что определение функции myGen () включает звездочку (*) рядом с ключевым словом функции. Это указывает среде выполнения, что это функция генератора.

Вернуться к коду

Вооружившись этими новыми знаниями, давайте закончим обзор нашего кода.

В getStartingValue () мы просто используем document.getElementById (), чтобы получить значение, введенное пользователем в текстовое поле формы. Если значение не было передано, переменная _counter инициализируется значением 0.

createIterator () затем делает то, что подразумевает его название. Он проверяет, определен ли уже итератор, а если нет, вызывает myGen (), который возвращает новый итератор.

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

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

Наконец, в nextVal () мы вызываем метод next () итератора и записываем значение в консоль.

Если мы введем значение 20 в текстовое поле, мы получим следующий вывод в консоли после нескольких щелчков кнопок.

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

Я обновил интерфейс, чтобы пользователь мог вводить начальное и конечное значения. Вот пользовательский интерфейс и обновленный HTML.

А вот обновленный код:

Большая часть кода осталась прежней, но с некоторыми изменениями. Во-первых, мы получаем как начальное, так и конечное значение из пользовательского интерфейса. Эти значения фиксируются в функции getStartingValue () и сохраняются в переменных _start и _end соответственно.

Затем мы используем эти переменные _start и _end при создании итератора в функции myGen ().

Наконец, мы используем новый синтаксис цикла for-of в строке 38, чтобы перебирать значения и записывать их в консоль.

Вы можете подумать: а почему бы мне просто не использовать для этого цикл while? Что ж, ответ в том, что вы можете. Обратной стороной использования цикла while является то, что вы должны будете вызывать метод next () на итераторе во время каждого цикла. Языковая функция for-of сделает это за вас. Это немного синтаксической магии, которую мы получаем бесплатно.

Заворачивать

В этом посте мы обсудили новую функциональность генератора в ES6. Генераторы работают вместе с итераторами, чтобы предоставить последовательность значений вызывающему коду. Уникальность генераторов в том, что они приостанавливают свое выполнение, когда возвращают значение, и возобновляют работу с того места, где они остановились, при повторном вызове. Использование вместе с новым синтаксисом for-of упрощает создание последовательностей данных, которые мы можем извлекать из одного элемента за раз.

Попробуйте и воспользуйтесь преимуществами этой новой функциональности в своем коде.

Ознакомьтесь с MDN здесь для получения документации по генераторам.

Спасибо за прочтение!