Iterator helpers are lazy, chainable transformations over any iterator — map, filter, take, drop, and flatMap that pull one value at a time instead of building a new array at each step. This is the model behind the ES2025 Iterator Helpers that ship on Iterator.prototype. Because nothing runs until a value is pulled, the same chain that transforms a three-item array can also run over an infinite sequence and stop the moment it has enough.
You will build iteratorHelpers, a function that wraps any iterable — an array, a Set, a generator, even a generator that never ends — and returns a helper with lazy, chainable methods plus terminal methods that actually consume.
iteratorHelpers(iterable); // -> a lazy, chainable, iterable helper
// Lazy — each returns a NEW helper and does no work until pulled:
// .map(fn) transform each value
// .filter(pred) keep values where pred is truthy
// .take(n) yield at most the first n values, then stop
// .drop(n) skip the first n values, yield the rest
// .flatMap(fn) fn returns an iterable per value; flatten one level
// Terminal — these pull values and produce a result:
// .toArray() collect every value into an array
// .forEach(fn) call fn on each value
// .reduce(fn, init) fold to a single value
// The helper is itself iterable, so you can for..of it or spread it.
iteratorHelpers([1, 2, 3, 4, 5])
.filter((x) => x % 2 === 1)
.map((x) => x * 10)
.toArray();
// [10, 30, 50]
// Works on an INFINITE source, because take stops pulling after 3:
function* naturals() {
let i = 1;
while (true) yield i++;
}
iteratorHelpers(naturals())
.map((x) => x * x)
.take(3)
.toArray();
// [1, 4, 9] (never tries to read the whole sequence)
.map().filter().take() does no work. Values flow one at a time only when a terminal method or for..of asks for them.take(n) must stop the source — on an infinite iterator, take(3) has to yield three values and then pull no more, or the program hangs. This is the crux of the question.flatMap flattens one level — fn returns an iterable per value; you flatten those by one level, not recursively.for..of), it is exhausted, just like a native iterator.Symbol.asyncIterator; everything here is synchronous and pull-based.