Освой протоколы, стоящие за for...of, спред-оператором и деструктуризацией, а затем используй генераторы для ленивых последовательностей.
Открыть этот урок в KodokonИтерацией управляют два разных протокола. Объект является итерируемым, если он предоставляет метод по ключу Symbol.iterator; этот метод должен возвращать итератор, то есть объект с методом next(), возвращающим { value, done }. Всё, что потребляет последовательности - for...of, спред-оператор ..., деструктуризация, Array.from, Promise.all - проходит через эти протоколы. Поэтому ты можешь сделать совместимым любой объект.
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range]); // [1, 2, 3]Писать итераторы руками многословно. Генераторы (function*) создают итератор автоматически: каждый yield приостанавливает выполнение и сохраняет локальное состояние функции. Эта ленивая модель вычислений позволяет описывать бесконечные последовательности - при условии, что потребитель ограничивает количество запрашиваемых значений.
function* naturals() {
let n = 0;
while (true) yield n++;
}
function* take(iterable, count) {
let index = 0;
for (const value of iterable) {
if (index >= count) return;
yield value;
index += 1;
}
}
console.log([...take(naturals(), 4)]); // [0, 1, 2, 3]Итераторы генераторов также предоставляют return() и throw(). Деталь спецификации, которую часто упускают из виду: когда ты выходишь из цикла for...of через break, return или исключение, движок автоматически вызывает iterator.return(). В генераторе при этом выполняются отложенные блоки finally - идеальное место, чтобы освободить ресурс (файл, соединение, блокировку).
function* readLines() {
try {
yield "line 1";
yield "line 2";
} finally {
console.log("cleanup");
}
}
for (const line of readLines()) {
console.log(line);
break; // triggers return(), hence the finally
}
// line 1, cleanuplength и числовых индексовSymbol.iterator, который возвращает объект с next()Array.prototypenext() напрямуюfor...of через break, пока он потребляет генератор?throw() у итератораreturn(), что запускает блоки finally генератораnext()