Stop turning everything into arrays (and do less work instead)

2 min read
0 views

Most front-end code processes data long before it ever hits the screen. We fetch a list, tweak it, trim it down, and repeat. And usually without thinking too hard about how much work we’re doing along the way.

For years, modern JavaScript has pushed us toward a familiar pattern:

data
  .map(...)
  .filter(...)
  .slice(...)
  .map(...)

It’s readable and expressive. It’s also eager, allocates multiple arrays, and often does unnecessary work.

Iterator helpers in JavaScript gives us a native, lazy alternative that’s especially relevant for dealing with large datasets, streams, and UI-driven logic.

Arrays everywhere (and way more work than necessary)

Consider this UI scenario:

  • You fetch a large dataset
  • You filter it
  • You take the first few results
  • You render them
const visibleItems = items
  .filter(isVisible)
  .map(transform)
  .slice(0, 10);

Looks harmless, right? Under the hood:

  1. filter creates a new array
  2. map creates another array
  3. slice creates yet another array

Even if you only need 10 items, you might’ve processed thousands. That mismatch between what you describe and what actually runs is where iterator helpers pay off.

So, what are iterator helpers?

Iterator helpers are chainable methods on iterator objects, not arrays.

That distinction matters: arrays don’t gain these methods directly. You need an iterator from values(), keys(), entries(), or a generator. Then you can build a lazy pipeline on top of it.

They let you do things like:

  • map
  • filter
  • take
  • drop
  • flatMap
  • find, some, every
  • reduce
  • toArray

Most of these helpers are lazy, meaning they only pull values as needed.

In general, laziness means:

  • No intermediate arrays
  • No unnecessary work
  • Computation stops as soon as it can

You describe what you want, and the runtime pulls values only when needed.

Lazy by default

Here’s the same logic with iterator helpers:

const visibleItems = items
  .values()
  .filter(isVisible)
  .map(transform)
  .take(10)
  .toArray();

What changed?

  • items.values() gives you an iterator, not an array
  • Each step runs only when the next value is requested
  • Processing stops after 10 matches

What this buys you in real apps

It’s not always about raw speed, it’s about avoiding unnecessary work. Iterator helpers unlock better UI patterns.

Rendering large lists

If you’re dealing with:

  • Virtualized lists
  • Infinite scrolling
  • Large tables

Lazy iteration means you don’t process items that never reach the screen:

function* rows(data) {
  for (const row of data) {
    yield renderRow(row);
  }
}

const visibleRows = rows(data)
  .filter(isInViewport)
  .take(20)
  .toArray();

You render exactly what you need and nothing more.

Streaming & async data

Async iterables have their own iterator helpers, which makes them a great fit for paginated APIs and streams:

async function* fetchPages() {
  let page = 1;
  while (true) {
    const res = await fetch(`/api/items?page=${page++}`);
    if (!res.ok) return;
    yield* await res.json();
  }
}

const firstTen = await fetchPages()
  .filter(isValid)
  .take(10)
  .toArray();

No buffering entire responses, no manual counters. Just describe the pipeline and let the runtime pull what’s needed.

Cleaner data pipelines (without utility libraries)

Before iterator helpers, you might’ve reached for libraries just to get lazy pipelines. Now it’s part of the language:

const ids = users
  .values()
  .map(u => u.id)
  .filter(Boolean)
  .toArray();

Readable, native, zero dependencies.

Iterator helpers vs. array methods

Array methods Iterator helpers
Eager Lazy
Allocate new arrays Minimal allocations
Always process all items Can stop early
Familiar Slight learning curve

When not to use iterator helpers

Iterator helpers are powerful, but they’re not a replacement for arrays everywhere. They’re a poor fit when:

  • You need random access (items[5])
  • You rely heavily on array mutation
  • Your data size is small and simplicity wins

Gotchas you should know about

Iterator helpers behave differently than arrays in a few important ways.

Gotcha What it means Why it matters
One-shot iterators Once consumed, they’re done You can’t reuse the same pipeline twice
Lazy execution Nothing runs until consumption Side effects may appear “missing”
Sequential only No random access Patterns like items[5] don’t translate
Debugging consumes data Logging can advance the iterator console.log can change behavior

Can I use this today?

Iterator helpers are supported in all modern browser (Chrome 122+, Firefox 131+, Safari 18.4+, Edge 122+) and Node.js 22+.

Doing less work on purpose

For a long time JavaScript trained us to eagerly turn everything into arrays. Iterator helpers give us another option:

  • Do less work
  • Allocate less memory
  • Write pipelines that match how UIs actually behave

And once you get used to lazy iteration, going back to eager chains feels a bit wasteful.