function*

Оголошення function* (ключове слово function з зірочкою) визначає функцію-генератор, яка повертає об'єкт Generator.

Ви також можете визначати функції-генератори за допомогою конструктора GeneratorFunction або функціонального виразу.

Синтаксис

function* name([param[, param[, ... param]]]) {
   statements
}
name
Ім'я функції.
param
Ім'я формального параметра функції.
statements
Інструкції, що складають тіло функції.

Опис

Генератори - це функції, з яких можна вийти та пізніше повторно зайти. Їхній контекст (зв'язування змінних) збережеться між заходами.

Генератори у JavaScript -- особливо у поєднанні з промісами -- дуже потужний інструмент асинхронного програмування, бо вони пом'якшують -- якщо не усувають повністю -- проблеми зворотних викликів, такі як Пекло зворотних викликів та Інверсія управління.

Виклик функції-генератора не виконує тіло функції негайно; замість цього повертається об'єкт-ітератор для функції. Коли викликається метод ітератора next(), тіло функції-генератора виконується до першого виразу yield, який визначає значення, що має бути повернене ітератором, або, оператором yield*, делегується до іншої функції-генератора. Метод next() повертає об'єкт з властивістю value, що містить отримане значення, та властивістю done, яка вказує, чи генератор віддав останнє значення, у вигляді булевого значення. Виклик метода next() з аргументом відновить виконання функції-генератора, замінюючи вираз yield в тій точці, де виконання було призупинене, аргументом з next().

Оператор return у генераторі змусить генератор завершити виконання (тобто, властивості done поверненого об'єкта буде присвоєне значення true). Якщо повертається значення, воно буде присвоєте властивості value об'єкта, що повертається генератором.
Схоже на оператор return, викидання помилки всередині генератора змусить генератор завершити виконання -- якщо не буде перехоплене у тілі генератора.
Коли генератор завершує виконання, наступні виклики next не виконають жодного коду генератора, вони лише повернуть об'єкт наступного вигляду: {value: undefined, done: true}.

Приклади

Простий приклад

function* idMaker() {
  var index = 0;
  while (true)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// ...

Приклад з yield*

function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function* generator(i) {
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20

Передача аргументів у об'єкти Generator

function* logGenerator() {
  console.log(0);
  console.log(1, yield);
  console.log(2, yield);
  console.log(3, yield);
}

var gen = logGenerator();

// перший виклик метода next виконує функцію з початку
// до першого оператора yield
gen.next();             // 0
gen.next('крендель');   // 1 крендель
gen.next('кава');       // 2 кава
gen.next('майонез');    // 3 майонез

Оператор return у генераторі

function* yieldAndReturn() {
  yield "Y";
  return "R";
  yield "недосяжний";
}

var gen = yieldAndReturn()
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }

Генератор як властивість об'єкта

const someObj = {
  *generator () {
    yield 'а';
    yield 'б';
  }
}

const gen = someObj.generator()

console.log(gen.next()); // { value: 'а', done: false }
console.log(gen.next()); // { value: 'б', done: false }
console.log(gen.next()); // { value: undefined, done: true }

Генератор як метод класу

class Foo {
  *generator () {
    yield 1;
    yield 2;
    yield 3;
  }
}

const f = new Foo ();
const gen = f.generator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

Генератор як обчислювана властивість

class Foo {
  *[Symbol.iterator] () {
    yield 1;
    yield 2;
  }
}

const SomeObj = {
  *[Symbol.iterator] () {
    yield 'а';
    yield 'б';
  }
}

console.log(Array.from(new Foo)); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'а', 'б' ]

Генератори не є конструкторами

function* f() {}
var obj = new f; // викидає "TypeError: f is not a constructor

Генератор, визначений у виразі

const foo = function* () {
  yield 10;
  yield 20;
};

const bar = foo();
console.log(bar.next()); // {value: 10, done: false}

Специфікації

Специфікація Статус Коментар
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'function*' in that specification.
Standard Початкове визначення.
ECMAScript 2016 (ECMA-262)
The definition of 'function*' in that specification.
Standard Внесено зміни, що генератори не повинні мати пастки [[Construct]] та викидатимуть помилку при використанні з new.
ECMAScript (ECMA-262)
The definition of 'function*' in that specification.
Living Standard

Сумісність з веб-переглядачами

BCD tables only load in the browser

Примітки щодо Firefox

Генератори та ітератори у Firefox до 26-ї версії

Старші версії Firefox реалізують старшу версію пропозиції генераторів. У старшій версії генератори визначались за допомогою звичайного ключового слова function (без зірочки), серед інших відмінностей. Дивіться більше інформації у статті Застаріла функція-генератор.

Замість викидання помилки повертається об'єкт IteratorResult

Починаючи з Gecko 29 (Firefox 29 / Thunderbird 29 / SeaMonkey 2.26), завершена функція-генератор більше не викидає помилку TypeError "generator has already finished". Замість цього, вона повертає об'єкт IteratorResult у вигляді { value: undefined, done: true } (bug 958951).

Див. також