JavaScript. Подробное руководство, 6-е издание, стр. 153

Тот факт, что генераторы имеют метод

<b>next(),</b>
который может возбуждать исключение
<b>StopIteration</b>
, явственно говорит о том, что они являются итераторами. [22] В действительности они являются итерируемыми итераторами, т. е. они могут использоваться в циклах
<b>for/in</b>
. Следующий пример демонстрирует, насколько просто создавать функции-генераторы и выполнять итерации по значениям, которые они возвращают с помощью инструкции
<b>yield</b>
:

<b>// Определение функции-генератора для выполнения итераций</b>

<b>// по целым числам в определенном диапазоне</b>

<b>function range(min, max) {</b>

<b>  for(let і = Math.ceil(min); і &lt;= max; i++) yield i;</b>

<b>}</b>

<b>// Вызвать функцию-генератор, чтобы получить генератор, и выполнить итерации по нему, </b>

<b>fог(let n in range(3.8)) console.log(n); // Выведет числа от 3 до 8.</b>

Функции-генераторы могут никогда не завершаться. Каноническим примером использования генераторов является воспроизведение последовательности чисел Фибоначчи:

<b>// Функция-генератор, которая воспроизводит последовательность чисел Фибоначчи</b>

<b>function fibonacci() {</b>

<b>  let х = 0, у = 1;</b>

<b>  while(true) {</b>

<b>    yield у;</b>

<b>    [х,у] = [у,х+у];</b>

<b>  }</b>

<b>}</b>

<b>// Вызвать функцию-генератор, чтобы получить генератор,</b>

<b>f = fibonacci();</b>

<b>// Использовать генератор как итератор, вывести первые 10 чисел Фибоначчи,</b>

<b>for(let і = 0; і &lt; 10; і++) console.log(f.next());</b>

Обратите внимание, что функция-генератор

<b>fibonacci()</b>
никогда не завершится. По этой причине создаваемый ею генератор никогда не возбудит исключение
<b>StopIteration</b>
. Поэтому, вместо того чтобы использовать его как итерируемый объект в цикле
<b>for/in</b>
и попасть в бесконечный цикл, мы используем его как итератор и явно вызываем его метод
<b>next()</b>
десять раз. После того как фрагмент выше будет выполнен, генератор f по-прежнему будет хранить информацию о состоянии функции-генератора. Если в программе не требуется далее хранить эту информацию, ее можно освободить вызовом метода
<b>close()</b>
объекта f:

<b>f.close();</b>

При вызове метода

<b>close()</b>
генератора производится завершение связанной с ним функции-генератора, как если бы она выполнила инструкцию
<b>return</b>
в той точке, где ее выполнение было приостановлено. Если это произошло в блоке
<b>try</b>
, автоматически будет выполнен блок
<b>finally</b>
перед тем, как
<b>close()</b>
вернет управление. Метод
<b>close()</b>
никогда не возвращает значение, но если блок
<b>finally </b>
возбудит исключение, оно продолжит свое распространение из вызова
<b>close().</b>

Генераторы часто бывает удобно использовать для последовательной обработки данных - элементов списка, строк текста, лексем в лексическом анализаторе и т.д. Генераторы можно объединять в цепочки, подобно конвейеру команд в Unix. Самое интересное в этом подходе заключается в том, что он следует принципу отложенных вычислений: значения «извлекаются» из генератора (или из конвейера генераторов) по мере необходимости, а не все сразу. Эту особенность демонстрирует пример 11.1.

Пример 11.1. Конвейер генераторов

<b>// Генератор, возвращающий строки текста по одной.</b>

<b>// Обратите внимание, что здесь не используется метод s.split(), потому что </b>

<b>// он обрабатывает текст целиком, создает массив, тогда как нам необходимо </b>

<b>// реализовать отложенную обработку, </b>

<b>function eachline(s) {</b>

<b>  let р;</b>

<b>  while((p = s.indexOf( \n')) != -1) {</b>

<b>    yield s. substrings, p);</b>

<b>    s = s.substring(p+1);</b>

<b>  }</b>

<b>  if (s.length &gt; 0) yield s;</b>

<b>}</b>

<b>// Функция-генератор, возвращающая f(x) для каждого элемента х итерируемого объекта і</b>

<b>function map(і. f) {</b>

<b>  fоr(let x in i) yield f(x);</b>

<b>}</b>

<b>// Функция-генератор, возвращающая элементы і, для которых f(x) возвращает true</b>

<b>function select(i, f) {</b>

<b>  for(let x in i) {</b>

<b>    if (f(x)) yield x;</b>

<b>  }</b>

<b>}</b>

<b>// Обрабатываемый текст</b>

<b>let text = &quot; «comment \n \n hello \nworld\n quit \n unreached \n&quot;;</b>