Генераторы появились вместе со стандартом ES6 в 2015 году, и к этому времени уже поддерживаются всеми современными браузерами. В документации об итераторах и генераторах на MDN написано вроде бы и подробно, но не очень понятно с точки зрения именно практического применения этих языковых конструкций.

Генераторы (Generators)— это особый тип функций, которые могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени, что позволяет запускать другой код в момент приостановления функции. Для объявления генератора используется синтаксическая конструкция: function* (функция со звёздочкой).

Для чего нужны генераторы?

Генераторы нужны для:

  1. Производства ряда значений по требованию, например, бесконечных последовательностей типа чисел Фибоначчи
  2. Добавления функционала итерабельности любому объекту с помощью метода Symbol.iterator
  3. Выполнения асинхронных операций с acync ... await

Объявление генератора

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

При вызове функции-генератор возвращается специальный объект, так называемый «генератор», для управления её выполнением. При вызове он запускает выполнение кода до ближайшей инструкции yield <значение>. По достижении yield выполнение функции приостанавливается, а соответствующее значение – возвращается во внешний код.

Генераторы отлично работают с итерируемыми (перебираемыми) объектами. В этом случае функция next(), как правило, автоматически вызывается в методе Symbol.iterator() для цикличного повторения неких действий, например в цикле for...of.

Для объявления генератора используется специальная синтаксическая конструкция: function*, которая так и называется -  "функция-генератор".

Нельзя объявить генератор с помощью стрелочной функции, будет ошибка:

Если внимательно присмотреться ко всем вариантам объявления генераторов, то вывод такой: это обычная функция перед именем которой стоит *

Метод next() генератора

Основным методом генератора является next(). Работа с ним состоит из таких частей:

  • Вызов функции-генератора мы записываем в переменную, а затем для нее вызываем метод next().
  • Метод next() находит в функции-генераторе yield  и возвращает нам объект в виде  {value: somevalue, done: false}. И так будет продолжаться до тех пор при вызове next(), пока done не будет равно true.
  • При вызове yield как бы говорит — передаём value и ставим паузу, пока не произойдёт следующий вызов next().

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

Используем этот подход для того, чтобы сделать сетку случайных цветов. Нажмите внизу на кнопку "Rerun" для обновления примера.

See the Pen Random Colors JS Generator by Elen (@ambassador) on CodePen.

Если вызвать функцию из этого кода со спред-оператором, то мы получим набор или массив цветовых кодов, например:

Еще один пример генератора - для создания счетчика обратного отсчета.

See the Pen Countdown by Elen (@ambassador)on CodePen.

Создание бесконечных последовательностей с помощью генераторов

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

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

See the Pen Fibonacci - JavaScript Generators by Elen (@ambassador) on CodePen.

Добавляем итерабельность в объект

Мы можем добавлять итераторы для неитерируемых, т.е. неперебираемых объектов. Это могут быть объекты какого-либо пользовательского класса. Для того чтобы перебирать эти объекты, мы добавляем к ним метод [Symbol.iterator] и задаем его вызов в виде функции-генератора.

В примере ниже генератор *[Symbol.iterator]() создает итератор для перебора символов от 'A' до 'K' включительно. Мы используем метод String.fromCharCode() для преобразования числового кода символа в сам символ. Вы можете поменять начальный и конечный символ в коде самостоятельно. Например, строка const letterGenerator = new LetterRange('A', 'я') приведет уже к выводу значительно бОльшого количества символов.

See the Pen Generator in [Symbol.iterator] by Elen (@ambassador) on CodePen.

Пример использования генераторов для эффекта печатной машинки

See the Pen Typing effect using JavaScript - Day 10 of #30Days30Projects by Elen (@ambassador) on CodePen.

Асинхронные генераторы

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

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

Параллельные асинхронные запросы

Асинхронные генераторы позволяют делать параллельные асинхронные запросы, что улучшает производительность и сокращает время выполнения.

Обработка ошибок в асинхронных генераторах

Асинхронные генераторы также облегчают обработку ошибок в асинхронном коде, что делает их удобным инструментом для управления ошибками в асинхронных операциях.

В примере ниже один асинхронный генератор получает с ресурса JSONPlaceholder сначала имена пользователей и создает ссылку на загрузку постов. Т.е. при клике на имя пользователя, функция displayUserPosts вызывается с userId, и она загружает и отображает посты этого пользователя в блоке с классом posts. Кроме того, здесь  также обрабатываются возможные ошибки.

See the Pen Untitled by Elen (@ambassador) on CodePen.

Болше интересных примеров вы найдете в статье по ссылке (англ).

Заключение

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

Автор: Админ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *