Итераторы и генераторы
↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая
Зачем это на собесе
Лежит в основе
for...of, spread,async/await; спрашивают, как сделать объект итерируемым.
Объяснение
Итерируемый объект имеет метод [Symbol.iterator](), возвращающий итератор с методом next() → { value, done }.
const range = {
from: 1, to: 3,
[Symbol.iterator]() {
let cur = this.from, end = this.to;
return { next: () => cur <= end ? { value: cur++, done: false } : { value: undefined, done: true } };
},
};
[...range]; // [1, 2, 3]
for (const x of range) console.log(x);Генератор — функция с function*, приостанавливаемая yield; возвращает итератор.
function* range(a, b) { for (let i = a; i <= b; i++) yield i; }
function* ids() { let id = 1; while (true) yield id++; } // бесконечная последовательность, ленивость
const [a, b] = ids(); // берём нужное
function* dialog() {
const name = yield "Как вас зовут?"; // yield принимает значение из next(value)
return `Привет, ${name}`;
}
const g = dialog(); g.next(); g.next("Анна"); // { value: "Привет, Анна", done: true }
yield* otherGenerator(); // делегированиеАсинхронные:
async function* pages(url) { let next = url; while (next) { const r = await (await fetch(next)).json(); yield r.items; next = r.next; } }
for await (const items of pages("/api?page=1")) render(items);Применения: ленивые последовательности, пагинация, пошаговые процессы, корутины (redux-saga), потоки данных (ReadableStream — async-итерируемый), кастомная итерация структур данных (деревья).
async/await исторически реализовывали на генераторах + Promise.
Нюансы и подводные камни
- Генератор одноразовый: после
done: trueповторный обход даёт пустой результат. return()иthrow()на итераторе позволяют завершать/прерывать (breakвfor...ofвызываетreturn).- Деструктуризация итерируемого потребляет его.
- Ленивые вычисления не выполняются, пока не запрошены.
- Итераторы-помощники (
Iterator.prototype.map/filter/take) появляются в новых версиях.
Практика
- Реализуйте генератор Фибоначчи и
take(n). - Сделайте итерируемое бинарное дерево.
- Постройте
for awaitдля постраничной загрузки.
Вопросы с ответами
Что такое итерируемый объект?
Объект с методом
[Symbol.iterator], возвращающим итератор сnext().
Чем генератор полезен?
Упрощает создание итераторов, даёт ленивые последовательности и пошаговое выполнение.