Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Функції-генератори». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Функція-генератор це особливий тип функції, яка вміє призупиняти своє виконання і згодом продовжувати його з того самого місця.** Оголошують її через `function*`, а всередині використовують `yield`, щоб віддавати значення по одному. Виклик такої функції не виконує тіло одразу, а повертає об'єкт-генератор, сумісний з протоколом ітератора, тож його можна обходити через `next()` або `for...of`. ```javascript function* gen() { yield 1; yield 2; } 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 } ``` **Ключове:** генератори дають ліниві обчислення, власні ітератори та покрокове керування виконанням, а `async function*` додає те саме для асинхронних потоків даних.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Функція-генератор (generator function) це функція, яка може призупинити своє виконання і потім продовжити його з того самого місця.** Вона повертає об'єкт-генератор і віддає значення по одному через `yield`, тому на ній зручно будувати ітератори, ліниві обчислення та асинхронні цикли. ## Теорія ### TL;DR - Оголошення має вигляд `function* name() {}`: зірочка після `function` робить функцію генератором. - Виклик не запускає тіло, а повертає об'єкт-генератор (ітератор) з методом `next()`. - `yield` віддає значення і «заморожує» виконання; наступний `next()` продовжує з того самого рядка. - Генератор сумісний з протоколом ітератора, тому працює у `for...of`, спреді та деструктуризації. - `next(value)` передає значення назад усередину генератора, тож обмін двосторонній. - `async function*` разом з `for await...of` дають те саме для асинхронних потоків (ES2018). ### Швидкий приклад ```javascript function* gen() { yield 1; yield 2; yield 3; } const iterator = gen(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true } ``` Що тут відбувається: - перший виклик `next()` доводить виконання до першого `yield`, повертає `1` і «заморожує» функцію; - наступний `next()` продовжує виконання з того самого місця; - після останнього `yield` генератор завершує роботу і віддає `done: true`. ### Синтаксис і об'єкт-генератор Функція-генератор це функція, яка повертає об'єкт-генератор (ітератор), а всередині себе використовує ключове слово `yield`, щоб призупиняти виконання і повертати значення по одному. ```javascript function* name(args) { // тіло функції } ``` - `*` після `function` робить функцію генератором; - `yield` використовують для покрокової видачі значень; - під час виклику функція не виконується одразу, а повертає генератор (ітератор). Кожен виклик `next()` повертає об'єкт виду `{ value, done }`, де `value` це те, що віддав `yield`, а `done` показує, чи генератор уже завершився. ### Генератори як ітератори Генератор повертає об'єкт, сумісний з протоколом ітераторів. Це означає, що його можна напряму передати в `for...of`: ```javascript function* numbers() { yield 10; yield 20; yield 30; } for (const n of numbers()) { console.log(n); } ``` Вивід: ```javascript 10 20 30 ``` Так само генератор працює зі спред-оператором (`[...numbers()]`) і з деструктуризацією масиву. ### Ліниві обчислення і нескінченні послідовності Генератори можуть бути нескінченними: вони не створюють весь масив одразу, а видають значення «на запит». Це і є ліниві обчислення, значення генеруються в міру потреби. ```javascript function* infiniteCounter() { let i = 1; while (true) { yield i++; } } const counter = infiniteCounter(); console.log(counter.next().value); // 1 console.log(counter.next().value); // 2 console.log(counter.next().value); // 3 // ...і так нескінченно ``` Той самий підхід зручний для скінченних послідовностей, які не варто матеріалізувати в масив: ```javascript function* range(start, end, step = 1) { for (let i = start; i <= end; i += step) { yield i; } } for (const num of range(1, 5)) { console.log(num); } ``` Вивід: ```javascript 1 2 3 4 5 ``` ### Двосторонній обмін: next(value) і return Дані можна передавати не тільки з генератора, а й назад у нього, через аргумент `next(value)`. Цей аргумент стає результатом того виразу `yield`, на якому генератор був зупинений. ```javascript function* dialog() { const name = yield 'Як тебе звати?'; yield `Привіт, ${name}!`; } const it = dialog(); console.log(it.next().value); // "Як тебе звати?" console.log(it.next('Maria').value); // "Привіт, Maria!" ``` Перший `next()` лише запускає генератор до першого `yield`, а другий передає `'Maria'` усередину як результат виразу `yield`. Завершити генератор достроково можна через `return`: ```javascript function* example() { yield 1; return 'кінець'; yield 2; // не виконається } const it = example(); console.log(it.next()); // { value: 1, done: false } console.log(it.next()); // { value: 'кінець', done: true } ``` ### Асинхронні генератори і де це застосовувати Асинхронні генератори (ES2018) дозволяють використовувати `await` всередині ітерованих потоків даних. Обходять їх через `for await...of`. ```javascript async function* fetchData() { yield await Promise.resolve('First chunk'); yield await Promise.resolve('Second chunk'); } for await (const part of fetchData()) { console.log(part); } ``` Навіщо генератори потрібні на практиці: | Застосування | Приклад | | --- | --- | | Створення ітераторів | Поелементна генерація даних | | Ліниві обчислення | Не потрібно тримати весь масив у пам'яті | | Покрокове виконання | Керування потоком виконання | | Асинхронні пайплайни | Обробка потоків даних без завантаження пам'яті | | Емуляція корутин | Можливість «зупинити» і «продовжити» код | Підсумок по синтаксису і можливостях: | Властивість | Опис | | --- | --- | | Визначення | Функція з `function*`, що повертає ітератор | | Повертає | Об'єкт-генератор | | Ключове слово | `yield` | | Можна призупиняти виконання | Так | | Можна передавати значення назад | Так | | Асинхронна версія | `async function*` | | Основне застосування | Ліниві обчислення, ітератори, асинхронні потоки | ### Типові помилки - **Думати, що виклик генератора виконує його тіло.** `gen()` лише створює об'єкт-генератор; жоден рядок тіла не виконається, доки ви не викличете `next()`. - **Плутати `yield` і `return`.** `return` завершує генератор: усе, що стоїть після нього, ніколи не буде віддано, а саме значення приходить разом з `done: true`. - **Чекати, що перший `next(value)` передасть значення всередину.** Перший виклик лише доводить виконання до першого `yield`, тому його аргумент ігнорується. - **Обходити нескінченний генератор через `for...of` без `break`.** Цикл не завершиться ніколи, потрібен явний обмежувач. - **Використовувати `for...of` замість `for await...of` для `async function*`.** У такому разі ви отримаєте проміси, а не самі значення. - **Перевикористовувати вичерпаний генератор.** Після `done: true` він не перезапускається: потрібно ще раз викликати функцію-генератор і отримати новий об'єкт.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.