Пошук уроків, статей та іншого контенту
Функції, які можна призупиняти й відновлювати — найзручніший спосіб писати ітератори та лінива послідовності.
Написання власного ітератора вручну (попередній урок) вимагає самостійно керувати змінною стану (current) між викликами next() — незручно й легко помилитись для складнішої логіки. Генератор — особливий тип функції, яка сама «пам'ятає», на якому місці зупинилась, і може призупиняти виконання, повертаючи проміжні значення одне за одним.
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator(); // виклик генератора НЕ виконує тіло одразу — повертає ітератор
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }Виклик генератора (numberGenerator()) нічого не виконує одразу — він повертає об'єкт-ітератор. Тіло функції виконується покроково, лише коли викликають next(), і зупиняється на кожному yield до наступного виклику.
Об'єкт, що повертає function*, автоматично реалізує протокол ітерації з попереднього уроку — тому генератори працюють із for...of без жодного додаткового коду:
for (const num of numberGenerator()) {
console.log(num); // 1, 2, 3
}function* range(from, to) {
for (let i = from; i <= to; i++) {
yield i;
}
}
[...range(1, 5)]; // [1, 2, 3, 4, 5]Порівняйте з версією через Symbol.iterator із попереднього уроку — той самий результат, набагато менше коду для керування станом.
Оскільки значення генеруються по одному, лише на вимогу, генератор може представляти теоретично нескінченну послідовність, не намагаючись обчислити її всю одразу (що для справжнього масиву було б неможливо):
function* infiniteCounter() {
let n = 1;
while (true) {
yield n++;
}
}
const counter = infiniteCounter();
counter.next().value; // 1
counter.next().value; // 2
counter.next().value; // 3
// можна викликати нескінченно — пам'ять не витрачається на "всі" значення одразуНіколи не використовуйте for...of чи spread напряму з нескінченним генератором без умови зупинки — це намагатиметься дістати всі значення одразу й зависне. Викликайте next() вручну, скільки потрібно, як у прикладі вище.
function* idGenerator() {
let id = 1;
while (true) {
yield `item-${id++}`;
}
}
const nextId = idGenerator();
nextId.next().value; // "item-1"
nextId.next().value; // "item-2"Очікування, що виклик генератора виконує тіло одразу — насправді виконання починається лише з першого next().
Використання spread чи for...of з нескінченним генератором без обмеження.
Забування, що кожен виклик функції-генератора створює НОВИЙ, незалежний ітератор — попередній прогрес не переноситься.
function* оголошує генератор — функцію, яка призупиняється на кожному yield і відновлюється при наступному виклику next(), автоматично реалізуючи протокол ітерації. Це прибирає ручне керування станом при написанні власних ітераторів і дозволяє природно виражати лінькі, потенційно нескінченні послідовності.
Спробуйте самостійно
Напишіть генератор fibonacciGenerator(), що видає числа Фібоначчі одне за одним (0, 1, 1, 2, 3, 5, 8...) нескінченно, і дістаньте перші 10 значень через ручні виклики next().