for...of, spread, деструктуризация массива, Array.from, new Map(entries), Promise.all работают с любым итерируемым объектом, а не только с массивами. За этим стоит простой протокол, который можно реализовать самому, а генераторы делают это в пару строк.
Протокол итерации
Итератор это объект с методом next(), который возвращает { value, done }. Когда done: true, перебор закончен.
Итерируемый объект (iterable) это объект с методом [Symbol.iterator](), который возвращает итератор.
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next: () => current <= last
? { value: current++, done: false }
: { value: undefined, done: true },
};
},
};
[...range]; // [1, 2, 3]
for (const n of range) {} // 1, 2, 3
Встроенные итерируемые: массивы, строки (по символам Unicode, а не по UTF-16 единицам), Map, Set, arguments, NodeList, типизированные массивы. Обычный объект {} не итерируемый: for...of по нему бросит TypeError, используйте Object.entries(obj).
Метод return() у итератора необязателен: for...of вызывает его при досрочном выходе (break, return, исключение), чтобы итератор мог освободить ресурсы.
Генераторы
Функция-генератор function* при вызове не выполняет тело, а возвращает объект-генератор. Он одновременно итератор и итерируемый. Каждый next() выполняет тело до следующего yield и приостанавливается.
function* idGenerator() {
let id = 1;
while (true) yield id++;
}
const ids = idGenerator();
ids.next().value; // 1
ids.next().value; // 2
Бесконечный цикл тут безопасен: код выполняется только по запросу. Это ленивые вычисления.
С генератором range выше превращается в:
const range = {
from: 1, to: 3,
*[Symbol.iterator]() {
for (let i = this.from; i <= this.to; i++) yield i;
},
};
Двусторонняя связь
gen.next(value): значение становится результатом выраженияyieldвнутри генератора. Первыйnext()только запускает генератор до первогоyield, его аргумент теряется.gen.return(value): завершает генератор, при этом выполняются блокиfinally.gen.throw(error): бросает ошибку в точкеyield, её можно пойматьtry/catchвнутри генератора.return xвнутри генератора даёт{ value: x, done: true }, ноfor...ofи spread это значение игнорируют.
yield*
yield* делегирует перебор другому итерируемому:
function* walk(tree) {
yield tree.value;
for (const child of tree.children) yield* walk(child);
}
Обход дерева, плоский список из вложенного, склейка последовательностей.
Асинхронная итерация
Асинхронный итератор возвращает из next() промис. Асинхронный генератор async function* может использовать и await, и yield. Перебирают их через for await...of.
async function* pages(url) {
while (url) {
const res = await fetch(url).then((r) => r.json());
yield res.items;
url = res.next;
}
}
for await (const items of pages('/api/orders')) render(items);
Так удобно читать постраничные API, потоки (ReadableStream и потоки Node итерируемы асинхронно) и события. Array.fromAsync (ES2024) собирает асинхронный итерируемый в массив.
Iterator helpers (ES2025)
У итераторов появились ленивые методы: map, filter, take, drop, flatMap, reduce, toArray, some, every, find, forEach. Они работают поштучно, без промежуточных массивов: ids.filter(isEven).take(5).toArray(). Поддержка: Node 22, Chrome, Firefox, Safari 18.4+.
Где это на практике
- Ленивые последовательности и бесконечные генераторы id.
- Обход деревьев и графов без рекурсивного накопления массивов.
- Постраничная загрузка и потоки данных через
for await. - Redux-Saga построена на генераторах.
- До
async/awaitгенераторы с библиотекойcoиспользовали для асинхронного кода.async/awaitпо сути и есть генератор с автоматическим запуском.
Частые ошибки
for...ofпо обычному объекту.- Ожидание, что вызов генератора выполнит его тело.
- Повторный перебор уже исчерпанного генератора: он одноразовый.
- Передача значения в первый
next().
Что спросят на собесе
- Что такое итератор и итерируемый объект, как сделать объект итерируемым.
- Что такое генератор и чем
yieldотличается отreturn. - Как передать значение в генератор, что делает
yield*. - Чем
for...ofотличается отfor...in. - Что такое асинхронные итераторы и
for await.