Skip to main content

Оператор yield у генераторах

yield це оператор, який повертає значення з функції-генератора назовні та призупиняє її виконання до наступного виклику .next(). Коли генератор доходить до yield, він заморожується, а наступний .next() відновлює роботу одразу після того yield, де зупинився.

Теорія

TL;DR

  • yield віддає значення назовні у полі value і зупиняє функцію на місці.
  • Наступний .next() продовжує виконання з рядка після цього yield.
  • Це «тимчасовий return»: значення повернуто, але функція не завершена.
  • next(value) передає значення назад: воно стає результатом виразу yield.
  • yield* делегує виконання іншому генератору або будь-якому ітерованому об'єкту.
  • Завдяки yield обчислення ліниві: значення створюються лише на запит.

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

javascript
function* gen() { console.log('До першого yield'); yield 1; console.log('Між yield'); yield 2; console.log('Після другого yield'); } const it = gen(); console.log(it.next()); // { value: 1, done: false } console.log(it.next()); // { value: 2, done: false } console.log(it.next()); // { value: undefined, done: true }

Що відбувається покроково:

  1. gen() повертає об'єкт-ітератор, функція ще не запущена.
  2. it.next() запускає функцію, вона доходить до yield 1, повертає { value: 1 } і заморожується.
  3. Наступний it.next() продовжує з того самого місця, після yield 1.
  4. Після другого yield генератор знову заморожується.
  5. Коли приходить done: true, генератор завершив роботу.

yield як тимчасовий return

javascript
function* seq() { yield 'A'; yield 'B'; yield 'C'; } const it = seq(); console.log(it.next().value); // "A" console.log(it.next().value); // "B" console.log(it.next().value); // "C" console.log(it.next().done); // true

Різниця з return принципова: return завершує функцію остаточно, а yield лише віддає проміжне значення і залишає функцію живою, з усіма її локальними змінними та позицією виконання.

Передавання значення назад через next(value)

yield не тільки віддає дані назовні, а й може приймати значення назад, під час наступного виклику .next(value).

javascript
function* dialog() { const name = yield 'Як тебе звати?'; yield `Привіт, ${name}!`; } const chat = dialog(); console.log(chat.next().value); // "Як тебе звати?" console.log(chat.next('Maria').value); // "Привіт, Maria!"
  • Перший next() запустив генератор і дійшов до першого yield.
  • Другий next('Maria') передав 'Maria' у генератор, і це значення потрапило в const name.

Завершення генератора: return() і throw()

Генератор завершується, коли доходить до кінця тіла, або якщо викликати iterator.return(value). У другому випадку він одразу віддає done: true.

javascript
function* numbers() { yield 1; yield 2; } const it = numbers(); console.log(it.next()); // { value: 1, done: false } console.log(it.return('stop')); // { value: 'stop', done: true } console.log(it.next()); // { value: undefined, done: true }

Так само можна «вкинути» помилку прямо всередину генератора, у точку поточного yield, і перехопити її там звичайним try/catch:

javascript
function* gen() { try { yield 1; } catch (e) { console.log('Помилку перехоплено:', e.message); } } const it = gen(); it.next(); it.throw(new Error('Щось пішло не так')); // Помилку перехоплено: Щось пішло не так

yield*: делегування іншому генератору

Іноді один генератор має «передати керування» іншому. Для цього є yield*.

javascript
function* inner() { yield 'B'; yield 'C'; } function* outer() { yield 'A'; yield* inner(); // передає керування іншому генератору yield 'D'; } for (const val of outer()) { console.log(val); }

Вивід:

javascript
A B C D

По суті yield* це вкладений цикл: «просто віддай усі значення іншого генератора». Він працює не лише з генераторами, а з будь-яким ітерованим об'єктом, наприклад yield* [1, 2, 3].

Лінивість обчислень

yield робить генератори лінивими (lazy): значення обчислюються тільки на запит, а не заздалегідь.

javascript
function* range(start, end) { for (let i = start; i <= end; i++) { console.log('Генерую', i); yield i; } } for (const num of range(1, 3)) { console.log('Отримано:', num); }

Вивід:

javascript
Генерую 1 Отримано: 1 Генерую 2 Отримано: 2 Генерую 3 Отримано: 3

Видно, що генератор створює значення в міру потреби, а не зберігає весь діапазон одразу.

Підсумок:

ОсобливістьОпис
Що робитьПризупиняє виконання і повертає значення
ВідновленняЧерез .next()
Можна передавати значення назадТак, next(value)
Можна завершитиЧерез return()
Можна делегуватиЧерез yield*
ОбчисленняЛіниві
Працює тільки вfunction* (генераторах)

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

  • Використовувати yield поза генератором. У звичайній чи стрілковій функції це синтаксична помилка, yield існує лише всередині function*.
  • Чекати значення від першого next(value). Перший виклик лише доводить виконання до першого yield, тому переданий аргумент нікуди не потрапляє.
  • Плутати yield і yield*. yield inner() віддасть сам об'єкт-генератор одним значенням, а yield* inner() віддасть усі його значення по черзі.
  • Думати, що yield завершує функцію. Ні, він лише ставить її на паузу; завершує return або вичерпання тіла.
  • Забувати про try/catch навколо yield. Без нього it.throw() просто завершить генератор і пробросить помилку назовні.
  • Створювати весь масив заздалегідь там, де потрібен yield. Це втрачає головну перевагу генераторів, лінивість і економію пам'яті.

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

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

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