Обычные функции возвращают только одно-единственное значение (или ничего).
Генераторы - это специальный тип функции, который работает как фабрика итераторов.
Для объявления генератора используется новая синтаксическая конструкция: function*
(ключевое слово function
со звёздочкой).
function* gen() {
yield 1;
yield 2;
yield 3;
}
const iterator = gen()
Вызов генератора создает объект класса Generator
, который является итерируемым (iterable), т. е. его можно перебирать в цикле for..of
или просто вызывая метод next()
, при этом никакой код внутри генератора пока не выполняется.
Каждый вызов метода next()
у созданного итератора запустить выполнение кода до первого yield
, после чего будет возвращено значение, указанное в нем.
// 1
console.log(iterator.next())
// [2, 3]
console.log(...iterator)
В метод next
можно передать значение, которое будет возвращено yield
:
function* gen() {
let i = 0
while (true) {
const param = yield i
console.log(param)
if (param) {
break
}
i++
}
}
const iterator = gen()
iterator.next() // { value: 0, done: false }
iterator.next(true) // { value: 1, done: false }
iterator.next() // { value: undefined, done: true }
У генераторов есть метод throw
, который позволяет бросить исключение внутри генератора.
function* gen() {
try {
// в этой строке возникнет ошибка
let result = yield "Сколько будет 2 + 2?"; // (**)
alert("выше будет исключение ^^^");
} catch(e) {
alert(e); // выведет ошибку
}
}
let generator = gen();
let question = generator.next().value;
generator.throw(new Error("ответ не найден в моей базе данных")); // (*)
«Вброшенная» в строке (*)
ошибка возникает в строке с yield
(**)
. Далее она обрабатывается как обычно. В примере выше она перехватывается try..catch
и выводится.
Подробнее:
У генераторов есть метод return(value)
, которое останавливает работу генератора.
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next(); // { value: 1, done: false }
g.return('foo'); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }
Если return(value)
вызывает генератор, который находится в уже "завершённом" состоянии, генератор останется в "завершённом" состоянии. Если аргумент не был передан, свойство value
вернёт тот же объект, что и .next()
. Если аргумент был передан, он будет установлен как значение свойства value
возвращаемого объекта.
Подробнее: MDN.
Для генераторов есть особый синтаксис yield*
, который позволяет «вкладывать» генераторы один в другой (осуществлять их композицию). Пример:
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i
}
function* generatePasswordCodes() {
// 0..9
yield* generateSequence(48, 57)
// A..Z
yield* generateSequence(65, 90)
// a..z
yield* generateSequence(97, 122)
}
let str = ''
for(let code of generatePasswordCodes()) {
str += String.fromCharCode(code)
}
alert(str)
- Нельзя определить стрелочную функцию генератор, стандартом определен только синтаксис
function*