Iterator Helpers: Lazy Pipelines in JavaScript

Iterator Helpers: Lazy Pipelines in JavaScript

ES2025 added map, filter, take and drop to iterators. Learn how lazy pipelines skip intermediate arrays, handle infinite sequences, and where they do not help.

Martin Ferret

Martin Ferret

October 6, 2026

Chain three array methods and you allocate three arrays. Two of them exist only to be thrown away.

const names = users
  .filter(u => u.active) // new array
  .map(u => u.name)      // another one
  .slice(0, 10);         // and a third

You wanted ten names. You built a full copy of the active users first, then a full copy of their names, then kept 10 of them.

ES2025 fixes this, and the change to your code is one method call.

The shift: work on the iterator

Every array can hand you an iterator with values(). In ES2025, iterators have their own map, filter, take and drop.

const names = users.values()
  .filter(u => u.active)
  .map(u => u.name)
  .take(10)
  .toArray();

Same reading order, same result. No intermediate array at all.

The difference is that array methods are eager. Each one runs to completion before the next one starts. Iterator helpers are lazy: nothing happens until something asks for a value, and then exactly one value travels through the whole chain.

What lazy actually means

The clearest way to see it is to make the chain talk.

[1, 2, 3].values()
  .map(n => { console.log(n); return n * 2; })
  .take(1)
  .toArray();

// logs 1, and nothing else

toArray asks take for a value. take asks map. map asks the source. One value goes through, take has what it needs, and the pipeline stops. Elements 2 and 3 are never touched.

With array methods, map would have processed all three before slice even ran.

This is why take is the method that changes how you write code. It is a short circuit for the entire chain.

Infinite sequences become ordinary

Laziness means a source no longer has to be finite. A generator that never returns is now something you can filter and map like any other collection.

function* naturals() {
  let n = 1;
  while (true) yield n++;
}

naturals().filter(n => n % 3 === 0).take(4).toArray(); // [3, 6, 9, 12]

Try that with Array.prototype.filter and the tab freezes. Here it stops after finding the fourth multiple.

The full surface

Eleven helper methods on Iterator.prototype, plus one static.

Five are lazy and return another iterator, so they chain: map, filter, take, drop, flatMap.

Six consume the iterator and return a final value: toArray, reduce, forEach, some, every, find.

Iterator.from() wraps an iterable or an iterator-like object so you can start chaining on it.

You get these on anything whose prototype chain reaches Iterator.prototype, which covers more than arrays: generators, the iterators from Map and Set, the result of String.prototype.matchAll(), and NodeList.prototype.values().

document.querySelectorAll('li').values()
  .map(li => li.textContent)
  .take(5)
  .toArray();

Three things to know before you switch

An iterator is consumed once. An array can be read as many times as you like. A pipeline cannot.

const evens = [1, 2, 3, 4].values().filter(n => n % 2 === 0);

evens.toArray(); // [2, 4]
evens.toArray(); // []

The second call finds an exhausted iterator. If you need the result twice, keep the array.

Lazy is not automatically faster. Laziness saves work you would otherwise have done for nothing. On a short array you keep to the end, the per value machinery can cost more than the allocations it avoids. The wins are real when the data is large, when take or find lets you stop early, or when the source is expensive to produce.

The async versions are not here yet. The helpers that shipped are synchronous only. Async iterator helpers are a separate TC39 proposal still in progress, so a streaming response or an async generator still needs for await...of.

Support

Iterator helpers reached Stage 4 at the October 2024 TC39 plenary and shipped in ES2025. They have been Baseline Newly available since March 2025, with Chrome and Edge 122, Firefox 131 and Safari 18.4 as the floor. On the server, Node.js 22 and later.

What to take away

When a chain ends in slice, find or an early break, you are asking for a fraction of the work you are doing. Add .values() at the start and .toArray() at the end, and the pipeline stops computing as soon as you stop asking.

And once the source can be infinite, generators stop being a curiosity and start being a tool.

More certificates.dev articles

Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.