GeneratorFunction: примеры (JAVASCRIPT)
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:
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):
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* для делегирования:
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():
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 }Ленивая генерация бесконечной последовательности:
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