Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Оператор yield у генераторах». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`yield` це оператор, який повертає значення з функції-генератора назовні та призупиняє її виконання до наступного виклику `.next()`.** Коли генератор доходить до `yield`, він «заморожується»; наступний `.next()` відновлює роботу одразу після того `yield`, де зупинився. Це свого роду тимчасовий `return`: він віддає значення, але не завершує функцію. ```javascript function* seq() { yield 'A'; yield 'B'; } const it = seq(); console.log(it.next().value); // "A" console.log(it.next().value); // "B" console.log(it.next().done); // true ``` **Ключове:** `yield` працює тільки всередині `function*`, дає лінивість і двосторонній обмін через `next(value)`, а `yield*` делегує виконання іншому генератору.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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`.** Це втрачає головну перевагу генераторів, лінивість і економію пам'яті.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.