Коротка відповідь
juniorГенератор — це функція, що повертає об’єкт‑ітератор; він використовує ключове слово `yield` для паузи і повернення значень послідовно. Ітератор — це об’єкт із методом `next()`, що повертає `{value, done}`; він дозволяє послідовно обробляти дані без створення повного масиву. У JavaScript генератори часто застосовують для асинхронних потоків, lazy‑ленгвінгу та побудови складних ітераційних алгоритмів.
Повне пояснення
Що це і навіщо
Генератор – функція, що повертає об’єкт‑ітератор. Він дозволяє пауза виконання і повернення значень по одному, що корисно для обробки великих наборів даних без витоку пам’яті.
Ключові принципи
yield– оператор, що повертає значення і зупиняє функцію.next()– метод, що повертає об’єкт{value, done}.- Lazy evaluation – значення генеруються лише при виклику
next().
Як це працює
- Генератор створюється:
function* gen() { … }. - Виклик
gen()повертає ітератор. - Кожен виклик
it.next()виконує код до наступногоyieldі повертає значення. - Коли функція завершується,
done: true.
Практика і реалізація
function* range(start, end) {
for (let i = start; i <= end; i++) yield i;
}
const it = range(1, 3);
console.log(it.next()); // {value: 1, done: false}
Тестування
import { describe, it, expect } from 'vitest';
describe('range generator', () => {
it('yields correct values', () => {
const gen = range(1, 3);
expect(gen.next()).toEqual({value: 1, done: false});
expect(gen.next()).toEqual({value: 2, done: false});
expect(gen.next()).toEqual({value: 3, done: false});
expect(gen.next()).toEqual({value: undefined, done: true});
});
});
Дизайн‑рішення
- Для асинхронних потоків використовують
async generator(async function*). - Для потокових даних (наприклад, читання файлу) генератор дозволяє обробляти частини без завантаження всього в пам’ять.
Оптимізація та edge cases
- Не використовуйте генератор, коли потрібна швидка доступність всіх елементів (наприклад,
Array.from). - Перевіряйте
doneперед використаннямvalue, щоб уникнутиundefined. - У випадку великої кількості елементів, генератор зменшує використання пам’яті.
Альтернативи
- Array +
forEach– простіше, але потребує повного масиву. - Iterator без генератора – ручна реалізація
next().
Часті помилки
- Забудьте повернути
{value, done}– TypeScript видасть помилку. - Використання
returnзамістьyieldу генераторі – завершить ітерацію. - Не обробляйте
done: true– може призвести до нескінченного циклу. - Виклик
next()після завершення – повертає{value: undefined, done: true}. - Не використовувати
for...ofз генератором – це працює, але не показує{done}. - Занадто глибока рекурсія у генераторі – може викликати переповнення стеку.
- Відсутність
try/catchу асинхронному генераторі – помилки не будуть оброблені. - Використання
yield*без перевірки джерела – може призвести до помилок типу.
Cheatsheet
function* gen() { … }– створює генератор.const it = gen();it.next()– отримує{value, done}.for (const v of gen()) { … }– ітерує без ручного викликуnext().async function* asyncGen() { … }– асинхронний генератор.
Follow‑up питання
- Як працює
yield*? - Чому асинхронні генератори повертають проміси?
- Які переваги
for await...ofнад звичайнимfor...of?