Skip to main content

Що повертає виклик генератора

Виклик функції-генератора (function*) не запускає її код одразу: він повертає об'єкт-генератор, який є ітератором і керує виконанням цієї функції. Тобто ви отримуєте не результат, а засіб, щоб виконувати тіло функції порціями.

Теорія

TL;DR

  • gen() не виконує жодного рядка тіла, він створює об'єкт-генератор.
  • Цей об'єкт є ітератором: у нього є .next(), .throw() і .return().
  • Одночасно він є ітерованим об'єктом, бо має [Symbol.iterator]().
  • Кожен .next() повертає { value, done } і доводить виконання до наступного yield.
  • Щоб отримати всі значення одразу, генератор треба обійти: for...of, спред, Array.from.
  • Генератор не дорівнює результату функції: numbers() це не [1, 2].

Швидкий приклад

javascript
function* gen() { yield 1; yield 2; yield 3; } const iterator = gen(); // повертається об'єкт-генератор console.log(iterator); // Object [Generator] {}

Об'єкт iterator це не результат функції, а контролер, який дозволяє виконувати генератор покроково через .next(), .throw(), .return().

Два протоколи, які реалізує генератор

Об'єкт-генератор одночасно реалізує два протоколи:

  1. Протокол ітератора: метод next(), який повертає об'єкт з полями value і done.
  2. Протокол ітерованого об'єкта: метод [Symbol.iterator](), який повертає сам генератор.

Саме тому той самий об'єкт можна і крутити вручну через .next(), і передати у for...of.

javascript
function* gen() { yield 'A'; yield 'B'; } const it = gen(); console.log(it.next()); // { value: 'A', done: false } console.log(it.next()); // { value: 'B', done: false } console.log(it.next()); // { value: undefined, done: true }

Кожен виклик .next():

  • запускає генератор, якщо той «заморожений»;
  • виконує його до наступного yield або return;
  • повертає об'єкт { value, done }.

Генератор у for...of, спреді та Array.from

Оскільки генератор ітерований, його можна використовувати всюди, де очікується ітерований об'єкт:

javascript
function* gen() { yield 1; yield 2; yield 3; } const iterator = gen(); for (const n of iterator) { console.log(n); }

Вивід:

javascript
1 2 3

Так само працюють [...gen()] і Array.from(gen()). Важливо: обхід вичерпує генератор, тому повторний for...of по тому самому об'єкту не дасть жодного значення, потрібен новий виклик gen().

Три методи об'єкта-генератора

МетодЩо робить
.next(value)Відновлює виконання до наступного yield; повертає { value, done }
.throw(error)Кидає виняток усередину генератора (ловиться через try/catch)
.return(value)Завершує генератор і повертає { value, done: true }

Генератор це не результат функції

Про це найчастіше забувають:

javascript
function* numbers() { yield 1; yield 2; } const result = numbers(); // повертає не [1, 2], а об'єкт-генератор

Щоб отримати справжні значення, генератор треба обійти:

javascript
console.log([...numbers()]); // [1, 2]

або вручну викликати .next() кілька разів.

Схематично це виглядає так:

javascript
┌─────────────────────┐ function* numbers(){ yield 1; yield 2; }└────────┬────────────┘ │ виклик ┌───────────────────────────────┐ Object [Generator] │ ├── next() │ ├── throw() │ ├── return() │ └── [Symbol.iterator]() └───────────────────────────────┘

Підсумок:

Що повертає генераторОб'єкт-генератор
Запускається одразу?Ні
Чи можна керувати виконанням?Так, через .next()
Чи реалізує протокол ітератора?Так
Як повертає значення?Через yield
Чи можна використати у for...ofТак
Як повертає кінцеве значенняЧерез return або разом з done: true

Типові помилки

  • Очікувати масив. numbers() повертає об'єкт-генератор, а не [1, 2]; масив дає спред або Array.from.
  • Думати, що тіло вже виконалося. Навіть console.log на початку генератора не спрацює, доки не буде першого .next().
  • Двічі обходити той самий об'єкт-генератор. Після done: true він вичерпаний; щоб пройти знову, викликайте функцію-генератор ще раз.
  • Плутати значення yield і значення return. Те, що повернув return, приходить разом з done: true і не потрапляє у for...of.
  • Забувати, що .return() завершує генератор назавжди. Наступні .next() віддаватимуть лише { value: undefined, done: true }.
  • Перевіряти typeof gen(). Це завжди 'object', тому перевіряти тип так безглуздо; ознакою генератора є наявність методу next і [Symbol.iterator].

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.