BitlystBitlyst
PostsSeriesTopicsAbout

Bitlyst

Short, diagram-first posts on JavaScript, React & Next.js. Follow via RSS or the newsletter.

© 2026 Mohsen Fallahnejad

PostsSeriesTopicsLinksAboutRSSGitHubX
Posts/javascript
3 min read

JavaScript Generators & Iterators — Lazy Sequences Explained

Generators let you produce values on demand instead of all at once. Learn how iterators and generator functions work, and when to reach for them.

Mohsen Fallahnejad

Mohsen Fallahnejad

Jun 29, 2026Discuss

🧠 What's the Problem?#

When you build an array of 1,000,000 numbers, JavaScript allocates memory for all of them upfront — even if you only need the first 10.

js
const million = Array.from({ length: 1_000_000 }, (_, i) => i);
console.log(million[0]); // 0 — but you just allocated 1M slots

Generators solve this by producing values lazily — only when you ask for the next one.


⚙️ The Iterator Protocol#

An iterator is any object with a next() method that returns { value, done }.

js
const iter = [1, 2, 3][Symbol.iterator]();

iter.next(); // { value: 1, done: false }
iter.next(); // { value: 2, done: false }
iter.next(); // { value: 3, done: false }
iter.next(); // { value: undefined, done: true }

Anything that implements this protocol works with for...of, spread [...], and destructuring.


⚙️ Generator Functions#

A generator function uses function* and yield to pause and resume execution.

js
function* count() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = count();
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }

Each yield pauses the function. The next .next() call resumes it from exactly where it left off.


⚙️ Infinite Sequences — The Real Power#

Generators shine when the sequence has no fixed end.

js
function* naturals(start = 0) {
  while (true) {
    yield start++;
  }
}

function take(n, iter) {
  const result = [];
  for (const val of iter) {
    result.push(val);
    if (result.length === n) break;
  }
  return result;
}

take(5, naturals(1)); // [1, 2, 3, 4, 5]

No memory wasted. No upfront computation. The sequence runs only as far as you pull it.


⚙️ Passing Values In#

next(value) lets you send data back into a paused generator.

js
function* adder() {
  let total = 0;
  while (true) {
    const n = yield total;
    total += n;
  }
}

const acc = adder();
acc.next();    // { value: 0, done: false }  — primes the generator
acc.next(10);  // { value: 10, done: false }
acc.next(5);   // { value: 15, done: false }
acc.next(20);  // { value: 35, done: false }

✅ Real-World Use Cases#

Use caseWhy generators help
Paginated API fetchingPull next page only when needed
Infinite scroll dataProduce IDs lazily
Custom iteration orderFull control over for...of
Async coordinationasync function* for async streams
State machinesEach yield is a state transition

⚠️ Gotchas#

Generators are single-pass. Once exhausted, they don't reset.

js
const gen = count();
[...gen]; // [1, 2, 3]
[...gen]; // [] — already done

You can't use yield inside a regular callback inside a generator.

js
function* broken() {
  [1, 2, 3].forEach(n => yield n); // ❌ SyntaxError
}

function* works() {
  yield* [1, 2, 3]; // ✅ delegate to another iterable
}

Use yield* to delegate to another iterable — it flattens it into the outer generator.


🔍 Flow#

  1. 1

    Call generator fn

  2. 2

    Get iterator object

  3. 3

    Call .next()

  4. 4

    Resume from yield

  5. 5

    Return { value, done }

then it depends
done: false
  1. ↳yield produces value
  2. →pauses
done: true
  1. ↳function returned or threw

Final Takeaway: Generators let you describe a sequence without computing it all upfront. Reach for them when you need infinite sequences, lazy pipelines, or fine-grained control over iteration. They're the foundation of async iterators (for await...of) too.

Summary#

ConceptWhat it is
Iterator protocolObject with .next() returning { value, done }
function*Declares a generator function
yieldPauses execution, emits a value
yield*Delegates to another iterable
next(val)Resumes generator and passes a value in
for...ofConsumes any iterable automatically
Filed under#javascript#generators#iterators#es6

Was this worth your time?

One tap tells me what to write more of.

Mohsen Fallahnejad

Written by

Mohsen Fallahnejad

Frontend engineer explaining JavaScript, React & Next.js internals in posts you can finish over coffee. More about me

X ↗GitHub ↗LinkedIn ↗
← OlderReact memo vs useMemo vs useCallback — When to Use WhichNewer →React useEffect Dependencies — The Bug That Keeps Coming Back

Start the discussion

Questions, corrections, a better approach? I read and reply to every comment.

Read next

The $4.35 Item That Was Charged $4.34

Why 0.1 + 0.2 !== 0.3 in JavaScript, how floating-point math drops a cent when converting dollars to cents, and why money should be stored as integers.

Nov 3, 20265 min read

The Leaderboard Arrived Sorted. JavaScript Re-sorted It by User ID.

Why JavaScript object key order puts integer-like keys first in ascending order, how that silently reorders JSON from your API, and when to use an array or Map.

Nov 3, 20265 min read

The Settings Object That Came Back undefined

How automatic semicolon insertion (ASI) turns `return` followed by a newline into `return;`, and the lines where JavaScript refuses to add a semicolon at all.

Nov 2, 20265 min read

Get new posts in your inbox

No spam. Unsubscribe any time.

3 min left
Discuss👍 React
👍