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 itemdone→ 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:
- It pauses right there
- It hands you the value
- 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 & 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 🎁
-
Iterator Protocol = A promise to have
.next()returning{ value, done } -
Symbol.iterator = The magic key that makes objects loopable
-
Generator Functions = Easy-mode iterators using
function* -
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! 🎪
