Skip to content
← All posts

Oct 19, 2026

Stop paying for rows you never render.

Your dashboard table shows 25 rows, but the `.filter().map().slice()` chain processes all 50,000 first. ES2025 iterator helpers make the pipeline lazy — only the rows you render get touched.

JavaScriptES2025PerformanceDashboards

A dashboard table I worked on showed 25 rows per page. The code behind it processed 50,000.

const page = rows
  .filter(r => r.status === 'active')   // new array: 50,000 items
  .map(r => toRow(r))                   // new array: 50,000 items
  .slice(0, 25);                        // finally, the 25 we wanted

Three passes over the full dataset. Two throwaway intermediate arrays. All to render 25 rows. On a fast machine you never notice. On a mid-range phone loading a data-heavy view, you notice.

This is the eager tax: array methods do all the work up front, whether you need it or not.

Lazy instead

ES2025 gave iterators the same chainable API, but lazy — nothing runs until you pull a value:

const page = rows.values()
  .filter(r => r.status === 'active')   // pending
  .map(r => toRow(r))                   // pending
  .take(25)                             // stop after 25
  .toArray();                           // NOW it runs — 25 rows processed

No intermediate arrays. The pipeline pulls one item at a time through filter → map, and take(25) stops the whole thing after 25 matches. The other 49,975 rows are never touched.

Where this actually matters

Not everywhere. For a few hundred items, array methods are fine — arguably clearer. The iterator version earns its keep when:

  • Paginated tables over large datasets — the exact case above. You render a page, not the dataset.
  • Search-as-you-type — .filter(predicate).take(10) stops at 10 matches instead of scanning everything.
  • Chained transformations on chart data — aggregating thousands of points down to a few hundred for rendering.

The mental model: array methods answer "give me everything, then I'll pick." Iterator helpers answer "give me what I ask for, and stop when I say stop."

The details worth knowing

rows.values() turns any array into an iterator. Iterator.from() works on any iterable — Sets, Maps, generators. The method list mirrors arrays: map, filter, take, drop, flatMap, find, some, every, reduce, forEach, toArray.

One gotcha: iterators are single-use. Once consumed, they're done — like generators. If you need the result twice, call .toArray() once and reuse the array.

Support is broad now: Chrome 121+, Firefox 115+, Safari 16.4+, Node 22+. For older targets, it's a small polyfill.

The deeper point is the same one behind every performance fix I've shipped on dashboards: don't do work you don't need. Eager array chains do work for rows nobody will ever see. Laziness isn't cleverness here — it's just not paying for what you don't use.