Iterators and Generators

Back

Loading concept...

Collections: Iterators and Generators 🎠

The Magical Carousel Story

Imagine you’re at a carnival with a magical carousel. This carousel is special – it doesn’t spin all horses at once. Instead, it shows you one horse at a time, exactly when you ask for it.

That’s what iterators and generators do in JavaScript! They give you items one-by-one, on demand, like a polite waiter bringing dishes only when you’re ready.


What’s an Iterator? 🎯

Think of an iterator like a ticket counter at a theme park.

  • You walk up and say “Next please!”
  • The person hands you ONE ticket
  • You come back again and say “Next please!”
  • They give you another ticket
  • When tickets run out, they say “Done!”
// Simple example: getting items one by one
const fruits = ['apple', 'banana', 'cherry'];
const iterator = fruits[Symbol.iterator]();

console.log(iterator.next());
// { value: 'apple', done: false }

console.log(iterator.next());
// { value: 'banana', done: false }

console.log(iterator.next());
// { value: 'cherry', done: false }

console.log(iterator.next());
// { value: undefined, done: true }

See the pattern? Each .next() call gives you:

  • value → the current item
  • done → is the carousel finished?

The Iterator Protocol 📜

The “Iterator Protocol” is like a promise that every iterator makes:

“I will have a .next() method that returns { value, done }

That’s it! Simple, right?

Building Your Own Iterator

Let’s make a counting iterator – like a number machine at a bakery:

function createCounter(max) {
  let count = 0;

  return {
    next() {
      if (count < max) {
        return { value: count++, done: false };
      }
      return { value: undefined, done: true };
    }
  };
}

const counter = createCounter(3);
console.log(counter.next()); // { value: 0, done: false }
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { done: true }

Symbol.iterator: The Magic Key 🔑

Remember how arrays let you use for...of loops? There’s a secret reason – they have a special key called Symbol.iterator.

Think of Symbol.iterator as a magic keyhole. If an object has this key, JavaScript knows how to loop through it!

const myList = ['cat', 'dog', 'bird'];

// This works because arrays have Symbol.iterator
for (const pet of myList) {
  console.log(pet);
}
// cat
// dog
// bird

Making Your Own Object Iterable

Want your own object to work with for...of? Give it the magic key!

const toyBox = {
  toys: ['ball', 'doll', 'car'],

  [Symbol.iterator]() {
    let index = 0;
    const toys = this.toys;

    return {
      next() {
        if (index < toys.length) {
          return { value: toys[index++], done: false };
        }
        return { done: true };
      }
    };
  }
};

// Now this works!
for (const toy of toyBox) {
  console.log(toy);
}
// ball
// doll
// car

Generator Functions: The Easy Way! ✨

Writing iterators by hand is like building a car from scratch. Generators are like buying a ready-made car!

A generator is a special function that can pause and resume. It’s like a story that stops at each chapter until you say “continue!”

How to Spot a Generator

Generators have a tiny star * after the function keyword:

function* myGenerator() {
  // I'm a generator!
}

Your First Generator

function* countToThree() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = countToThree();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { done: true }

Notice: The generator automatically follows the iterator protocol! No need to write { value, done } yourself.


The yield Keyword: Pause Button ⏸️

yield is like pressing pause on a video game.

When the function hits yield:

  1. It pauses right there
  2. It hands you the value
  3. It waits until you call .next() again
function* bakery() {
  console.log('Making cookie...');
  yield 'cookie';

  console.log('Making cake...');
  yield 'cake';

  console.log('Making pie...');
  yield 'pie';

  console.log('Bakery closed!');
}

const shop = bakery();

console.log(shop.next().value);
// Making cookie...
// cookie

console.log(shop.next().value);
// Making cake...
// cake

See how it pauses between items? Each yield is a stopping point!


Real-World Example: ID Generator

Let’s build something useful – a generator that creates unique IDs:

function* createIdGenerator(prefix) {
  let id = 1;
  while (true) {
    yield `${prefix}-${id}`;
    id++;
  }
}

const userIds = createIdGenerator('USER');

console.log(userIds.next().value); // USER-1
console.log(userIds.next().value); // USER-2
console.log(userIds.next().value); // USER-3
// Goes on forever!

This generator is infinite – but that’s okay! It only creates IDs when you ask for them.


Generators + for…of = Perfect Match 💑

Generators automatically work with for...of:

function* rainbow() {
  yield 'red';
  yield 'orange';
  yield 'yellow';
  yield 'green';
  yield 'blue';
}

for (const color of rainbow()) {
  console.log(color);
}
// red
// orange
// yellow
// green
// blue

Making Objects Iterable with Generators

Remember making toyBox iterable earlier? Generators make it super simple:

const toyBox = {
  toys: ['ball', 'doll', 'car'],

  *[Symbol.iterator]() {
    for (const toy of this.toys) {
      yield toy;
    }
  }
};

for (const toy of toyBox) {
  console.log(toy);
}
// ball
// doll
// car

So much cleaner! The * before [Symbol.iterator] makes it a generator.


Quick Summary Diagram

graph TD A["Iterator Protocol"] --> B["Must have .next method"] B --> C["Returns value &amp; done"] D["Symbol.iterator"] --> E["Magic key for objects"] E --> F["Enables for...of loops"] G["Generator function*"] --> H["Uses yield keyword"] H --> I["Pauses and resumes"] I --> J["Auto-creates iterator"]

Key Takeaways 🎁

  1. Iterator Protocol = A promise to have .next() returning { value, done }

  2. Symbol.iterator = The magic key that makes objects loopable

  3. Generator Functions = Easy-mode iterators using function*

  4. yield = Pause button that hands out values one at a time


Remember This Analogy! 🎠

Iterators are like a carousel operator:

  • You ask for the next horse → .next()
  • They show you one horse → value
  • They tell you if there are more → done

Generators are like a story reader:

  • They read until a chapter ends → yield
  • They wait for you to say “continue” → .next()
  • They pick up right where they left off!

Now you can create your own magical carousels in JavaScript! 🎪

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.