В базе данных 10 миллионов записей. Если загрузить их все в массив сразу — памяти не хватит. Но если отдавать по одной «по требованию» — всё работает. Это и есть идея генераторов: производить значения лениво, только когда они нужны.
Обычная функция запускается, вычисляет всё сразу и возвращает результат. Генератор может приостанавливать выполнение на yield и возобновлять его позже. Это даёт ленивые вычисления и бесконечные последовательности без загрузки всего в память.
function* pipeline() {
console.log('Шаг 1: получение данных')
yield 'данные получены'
console.log('Шаг 2: валидация')
yield 'данные проверены'
console.log('Шаг 3: сохранение')
return 'готово'
}
const gen = pipeline() // тело не выполнялось!
gen.next() // 'Шаг 1: ...' → { value: 'данные получены', done: false }
gen.next() // 'Шаг 2: ...' → { value: 'данные проверены', done: false }
gen.next() // 'Шаг 3: ...' → { value: 'готово', done: true }
gen.next() // → { value: undefined, done: true }В генератор можно передавать значения через next(value):
function* accumulator() {
let total = 0
while (true) {
const amount = yield total // пауза, ждём следующее значение
if (amount === null) return total // сигнал завершения
total += amount
}
}
const acc = accumulator()
acc.next() // запуск: { value: 0, done: false }
acc.next(100) // { value: 100, done: false }
acc.next(250) // { value: 350, done: false }
acc.next(null) // { value: 350, done: true } — завершениеГенератор — итерируемый объект, работает в for...of и spread:
function* range(from, to, step = 1) {
for (let i = from; i <= to; i += step) yield i
}
for (const n of range(1, 5)) console.log(n) // 1 2 3 4 5
console.log([...range(0, 10, 2)]) // [0, 2, 4, 6, 8, 10]Генератор не обязан заканчиваться. Ленивая природа позволяет работать с бесконечными последовательностями:
function* naturals(start = 1) {
let n = start
while (true) yield n++
}
function* fibonacci() {
let [a, b] = [0, 1]
while (true) {
yield a;
[a, b] = [b, a + b]
}
}
// Берём только первые 5 чисел Фибоначчи — остальные не вычисляются
const fib = fibonacci()
for (let i = 0; i < 5; i++) console.log(fib.next().value)
// 0, 1, 1, 2, 3function* idGenerator(prefix = '') {
let id = 1
while (true) yield `${prefix}${id++}`
}
const userId = idGenerator('user-')
const orderId = idGenerator('order-')
userId.next().value // 'user-1'
userId.next().value // 'user-2'
orderId.next().value // 'order-1' — независимый счётчикfunction* gen() {
yield 1
return 'финал' // завершает генератор, done: true
yield 2 // никогда не выполнится
}
const g = gen()
g.next() // { value: 1, done: false }
g.next() // { value: 'финал', done: true }
g.next() // { value: undefined, done: true }Важно: for...of игнорирует значение из return — оно доступно только через next().
Ошибка 1: попытка вызвать генератор как обычную функцию
function* counter() {
yield 1; yield 2; yield 3
}
// Неправильно — получаем генератор-объект, а не число
const result = counter()
console.log(result) // Object [Generator] {} — не значения!
// Правильно
const gen = counter()
console.log(gen.next().value) // 1
console.log([...counter()]) // [1, 2, 3]Ошибка 2: переиспользование «исчерпанного» генератора
function* range(n) {
for (let i = 0; i < n; i++) yield i
}
const gen = range(3)
console.log([...gen]) // [0, 1, 2]
console.log([...gen]) // [] — генератор уже исчерпан!
// Правильно: создавай новый генератор для каждого использования
console.log([...range(3)]) // [0, 1, 2]
console.log([...range(3)]) // [0, 1, 2]Ошибка 3: бесконечный генератор в spread без ограничения
function* inf() { let n = 0; while (true) yield n++ }
// ОПАСНО — зависнет браузер/Node.js
const all = [...inf()] // пытается создать бесконечный массив
// Правильно — бери только нужное количество
const gen = inf()
const first10 = Array.from({ length: 10 }, () => gen.next().value)
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Генератор ID, range с шагом и ленивая пагинация данных
// 1. Генераторы уникальных ID — независимые счётчики
function* createIdGen(prefix = '', start = 1) {
let id = start
while (true) yield `${prefix}${id++}`
}
const userId = createIdGen('user-')
const orderId = createIdGen('order-', 1000)
console.log(userId.next().value) // 'user-1'
console.log(userId.next().value) // 'user-2'
console.log(orderId.next().value) // 'order-1000'
console.log(orderId.next().value) // 'order-1001'
// 2. Range генератор с шагом — ленивый диапазон
function* range(from, to, step = 1) {
for (let i = from; i <= to; i += step) yield i
}
console.log([...range(0, 10, 2)]) // [0, 2, 4, 6, 8, 10]
console.log([...range(1, 5)]) // [1, 2, 3, 4, 5]
// 3. Ленивая пагинация — симуляция подгрузки страниц по требованию
function* paginate(items, pageSize) {
let page = 0
while (page * pageSize < items.length) {
yield items.slice(page * pageSize, (page + 1) * pageSize)
page++
}
}
const products = Array.from({ length: 7 }, (_, i) => ({ id: i + 1, name: `Товар ${i + 1}` }))
const pages = paginate(products, 3)
const page1 = pages.next().value
const page2 = pages.next().value
console.log(page1.map(p => p.name)) // ['Товар 1', 'Товар 2', 'Товар 3']
console.log(page2.map(p => p.name)) // ['Товар 4', 'Товар 5', 'Товар 6']
// 4. Бесконечный Фибоначчи — берём первые N
function* fibonacci() {
let [a, b] = [0, 1]
while (true) {
yield a;
[a, b] = [b, a + b]
}
}
const fib = fibonacci()
const first8 = Array.from({ length: 8 }, () => fib.next().value)
console.log(first8) // [0, 1, 1, 2, 3, 5, 8, 13]В базе данных 10 миллионов записей. Если загрузить их все в массив сразу — памяти не хватит. Но если отдавать по одной «по требованию» — всё работает. Это и есть идея генераторов: производить значения лениво, только когда они нужны.
Обычная функция запускается, вычисляет всё сразу и возвращает результат. Генератор может приостанавливать выполнение на yield и возобновлять его позже. Это даёт ленивые вычисления и бесконечные последовательности без загрузки всего в память.
function* pipeline() {
console.log('Шаг 1: получение данных')
yield 'данные получены'
console.log('Шаг 2: валидация')
yield 'данные проверены'
console.log('Шаг 3: сохранение')
return 'готово'
}
const gen = pipeline() // тело не выполнялось!
gen.next() // 'Шаг 1: ...' → { value: 'данные получены', done: false }
gen.next() // 'Шаг 2: ...' → { value: 'данные проверены', done: false }
gen.next() // 'Шаг 3: ...' → { value: 'готово', done: true }
gen.next() // → { value: undefined, done: true }В генератор можно передавать значения через next(value):
function* accumulator() {
let total = 0
while (true) {
const amount = yield total // пауза, ждём следующее значение
if (amount === null) return total // сигнал завершения
total += amount
}
}
const acc = accumulator()
acc.next() // запуск: { value: 0, done: false }
acc.next(100) // { value: 100, done: false }
acc.next(250) // { value: 350, done: false }
acc.next(null) // { value: 350, done: true } — завершениеГенератор — итерируемый объект, работает в for...of и spread:
function* range(from, to, step = 1) {
for (let i = from; i <= to; i += step) yield i
}
for (const n of range(1, 5)) console.log(n) // 1 2 3 4 5
console.log([...range(0, 10, 2)]) // [0, 2, 4, 6, 8, 10]Генератор не обязан заканчиваться. Ленивая природа позволяет работать с бесконечными последовательностями:
function* naturals(start = 1) {
let n = start
while (true) yield n++
}
function* fibonacci() {
let [a, b] = [0, 1]
while (true) {
yield a;
[a, b] = [b, a + b]
}
}
// Берём только первые 5 чисел Фибоначчи — остальные не вычисляются
const fib = fibonacci()
for (let i = 0; i < 5; i++) console.log(fib.next().value)
// 0, 1, 1, 2, 3function* idGenerator(prefix = '') {
let id = 1
while (true) yield `${prefix}${id++}`
}
const userId = idGenerator('user-')
const orderId = idGenerator('order-')
userId.next().value // 'user-1'
userId.next().value // 'user-2'
orderId.next().value // 'order-1' — независимый счётчикfunction* gen() {
yield 1
return 'финал' // завершает генератор, done: true
yield 2 // никогда не выполнится
}
const g = gen()
g.next() // { value: 1, done: false }
g.next() // { value: 'финал', done: true }
g.next() // { value: undefined, done: true }Важно: for...of игнорирует значение из return — оно доступно только через next().
Ошибка 1: попытка вызвать генератор как обычную функцию
function* counter() {
yield 1; yield 2; yield 3
}
// Неправильно — получаем генератор-объект, а не число
const result = counter()
console.log(result) // Object [Generator] {} — не значения!
// Правильно
const gen = counter()
console.log(gen.next().value) // 1
console.log([...counter()]) // [1, 2, 3]Ошибка 2: переиспользование «исчерпанного» генератора
function* range(n) {
for (let i = 0; i < n; i++) yield i
}
const gen = range(3)
console.log([...gen]) // [0, 1, 2]
console.log([...gen]) // [] — генератор уже исчерпан!
// Правильно: создавай новый генератор для каждого использования
console.log([...range(3)]) // [0, 1, 2]
console.log([...range(3)]) // [0, 1, 2]Ошибка 3: бесконечный генератор в spread без ограничения
function* inf() { let n = 0; while (true) yield n++ }
// ОПАСНО — зависнет браузер/Node.js
const all = [...inf()] // пытается создать бесконечный массив
// Правильно — бери только нужное количество
const gen = inf()
const first10 = Array.from({ length: 10 }, () => gen.next().value)
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Генератор ID, range с шагом и ленивая пагинация данных
// 1. Генераторы уникальных ID — независимые счётчики
function* createIdGen(prefix = '', start = 1) {
let id = start
while (true) yield `${prefix}${id++}`
}
const userId = createIdGen('user-')
const orderId = createIdGen('order-', 1000)
console.log(userId.next().value) // 'user-1'
console.log(userId.next().value) // 'user-2'
console.log(orderId.next().value) // 'order-1000'
console.log(orderId.next().value) // 'order-1001'
// 2. Range генератор с шагом — ленивый диапазон
function* range(from, to, step = 1) {
for (let i = from; i <= to; i += step) yield i
}
console.log([...range(0, 10, 2)]) // [0, 2, 4, 6, 8, 10]
console.log([...range(1, 5)]) // [1, 2, 3, 4, 5]
// 3. Ленивая пагинация — симуляция подгрузки страниц по требованию
function* paginate(items, pageSize) {
let page = 0
while (page * pageSize < items.length) {
yield items.slice(page * pageSize, (page + 1) * pageSize)
page++
}
}
const products = Array.from({ length: 7 }, (_, i) => ({ id: i + 1, name: `Товар ${i + 1}` }))
const pages = paginate(products, 3)
const page1 = pages.next().value
const page2 = pages.next().value
console.log(page1.map(p => p.name)) // ['Товар 1', 'Товар 2', 'Товар 3']
console.log(page2.map(p => p.name)) // ['Товар 4', 'Товар 5', 'Товар 6']
// 4. Бесконечный Фибоначчи — берём первые N
function* fibonacci() {
let [a, b] = [0, 1]
while (true) {
yield a;
[a, b] = [b, a + b]
}
}
const fib = fibonacci()
const first8 = Array.from({ length: 8 }, () => fib.next().value)
console.log(first8) // [0, 1, 1, 2, 3, 5, 8, 13]Напиши генератор take(iterable, n), который принимает любой итерируемый объект (в том числе бесконечный генератор) и выдаёт только первые n элементов. Используй его для получения первых 5 чётных натуральных чисел из бесконечного генератора.
Используй return (без значения) для выхода из генератора при count >= n. Это остановит итерацию. Для чётных: take(evens(), 5). Генератор evens() уже определён в задаче.