GeneratorFunction: примеры (JAVASCRIPT)

JavaScript GeneratorFunction: полный обзор и примеры применения
Раздел: Итераторы, Генераторы
GeneratorFunction(...args: string, functionBody: string): GeneratorFunction

Основы GeneratorFunction

Конструктор GeneratorFunction создает новый объект функции-генератора в JavaScript. В отличие от обычных функций, функции-генераторы могут приостанавливать выполнение и возобновлять его позже. Этот конструктор не является глобальным объектом, но может быть получен через Object.getPrototypeOf(function*(){}).constructor.

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

Синтаксис: new GeneratorFunction([arg1[, arg2[, ...argN]],] functionBody)

  • arg1, arg2, ... argN: Имена аргументов, которые используются в функции как формальные параметры. Каждое имя должно быть строкой, соответствующей правилам именования идентификаторов JavaScript.
  • functionBody: Строка, содержащая JavaScript-операторы, составляющие тело функции.

Возвращаемое значение: новый объект функции-генератора. При вызове эта функция возвращает объект-генератор, который соответствует протоколу итератора. Объект-генератор имеет методы next(), return() и throw().

Простые примеры использования

Создание функции-генератора через конструктор:

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('a', 'b', 'yield a + b;');
const generator = genFunc(1, 2);
console.log(generator.next());
console.log(generator.next());
{ value: 3, done: false }
{ value: undefined, done: true }

Генератор с несколькими yield:

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('x', 'yield x; yield x*2; yield x*3;');
const gen = genFunc(5);
console.log([...gen]);
[5, 10, 15]

Использование return в генераторе:

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('yield 1; yield 2; return "finished";');
const gen = genFunc();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 'finished', done: true }

Альтернативные подходы в JavaScript

Function Declaration/Expression с function*: Более распространенный синтаксис создания генераторов. Предпочтительнее для статического кода.

function* simpleGen() {
  yield 1;
  yield 2;
}

Async Generator Functions: Функции-генераторы, возвращающие асинхронный итератор. Используются для работы с асинхронными потоками данных.

async function* asyncGen() {
  yield await Promise.resolve(1);
}

Итераторы без генераторов: Создание объектов с методом [Symbol.iterator]. Подходит для кастомных итерируемых объектов, но требует ручного управления состоянием.

Генераторы в других языках программирования

Python: Генераторы создаются с помощью ключевого слова yield в функциях. Конструктор types.GeneratorType существует, но редко используется напрямую.

def simple_gen():
    yield 1
    yield 2

gen = simple_gen()
print(next(gen))  # 1
print(next(gen))  # 2
1
2

PHP: Генераторы появились в PHP 5.5. Используется ключевое слово yield. Нет конструктора, аналогичного GeneratorFunction.

function simpleGen() {
    yield 1;
    yield 2;
}

$gen = simpleGen();
foreach ($gen as $value) {
    echo $value . "\n";
}
1
2

C#: Использует yield return в методах, возвращающих IEnumerable или IEnumerator. Нет динамического создания генераторов.

Распространенные ошибки

Использование new с function*: Синтаксис function* не требует new.

// Неправильно
const gen = new function*() { yield 1; };
// Правильно
function* genFunc() { yield 1; }
const gen = genFunc();

Путаница между GeneratorFunction и функцией-генератором: GeneratorFunction создает функцию, а не генератор. Непосредственный вызов возвращает генератор.

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('yield 1;');
// genFunc - это функция, а не генератор
const generator = genFunc(); // Теперь generator - объект-генератор
generator.next(); // { value: 1, done: false }

Игнорирование параметров конструктора: Все аргументы, кроме последнего, трактуются как имена параметров.

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('a', 'b', 'yield a + b;');
const gen = genFunc(3, 4);
console.log(gen.next().value); // 7

История изменений

Функции-генераторы и GeneratorFunction были добавлены в стандарт ECMAScript 2015 (ES6). С тех пор существенных изменений в API не было. В более поздних версиях ECMAScript были добавлены асинхронные генераторы и yield* для делегирования другому генератору, но это не затрагивает непосредственно конструктор GeneratorFunction.

Расширенные примеры

Динамическое создание генератора с переменным числом yield:

Пример javascript
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
function createSequenceGenerator(steps) {
  let body = '';
  for (let i = 0; i < steps; i++) {
    body += `yield ${i}; `;
  }
  return new GeneratorFunction(body);
}
const threeStepGen = createSequenceGenerator(3);
const gen = threeStepGen();
console.log([...gen]); // [0, 1, 2]
[0, 1, 2]

Генератор с передачей значения внутрь через next(value):

Пример javascript
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction('let x = yield 1; yield x + 2;');
const gen = genFunc();
console.log(gen.next());    // { value: 1, done: false }
console.log(gen.next(10));  // { value: 12, done: false }
console.log(gen.next());    // { value: undefined, done: true }
{ value: 1, done: false }
{ value: 12, done: false }
{ value: undefined, done: true }

Использование yield* для делегирования:

Пример javascript
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const innerGenBody = 'yield 2; yield 3;';
const outerGenBody = 'yield 1; yield* inner(); yield 4;';
const innerGenFunc = new GeneratorFunction(innerGenBody);
const outerGenFunc = new GeneratorFunction('inner', outerGenBody);
const inner = () => innerGenFunc();
const gen = outerGenFunc(inner);
console.log([...gen]); // [1, 2, 3, 4]
[1, 2, 3, 4]

Генератор с обработкой ошибок через throw():

Пример javascript
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const genFunc = new GeneratorFunction(`
  try {
    yield 1;
  } catch(e) {
    yield 'Ошибка: ' + e;
  }
`);
const gen = genFunc();
console.log(gen.next());        // { value: 1, done: false }
console.log(gen.throw('test')); // { value: 'Ошибка: test', done: false }
console.log(gen.next());        // { value: undefined, done: true }
{ value: 1, done: false }
{ value: 'Ошибка: test', done: false }
{ value: undefined, done: true }

Ленивая генерация бесконечной последовательности:

Пример javascript
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
const infiniteGen = new GeneratorFunction('i', 'while(true) yield i++;');
const gen = infiniteGen(100);
console.log(gen.next().value); // 100
console.log(gen.next().value); // 101
console.log(gen.next().value); // 102
100
101
102

JS GeneratorFunction function comments

En
GeneratorFunction Creates a new generator function object.