Bitlyst

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.

3 min read#javascript#generators#iterators#es6

🧠 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.

code
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 }.

code
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.

code
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.

code
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.

code
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.

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

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

code
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

Call generator fnGet iterator objectCall .next()Resume from yieldReturn { value, done }
done: falseyield produces valuepauses
done: truefunction 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

You made it to the end!

Did this help? Leave a reaction — it takes one second.

 

Got feedback? 💬

Typo, suggestion, question — I read every message.

Comments

Mohsen Fallahnejad
Mohsen Fallahnejad

Writing bite-sized JS, React & Next.js tips

Get new posts in your inbox

No spam. Unsubscribe any time.