Iterators and generators are closely related and often used together, they are not the same. Let’s break it down clearly.
🔁 Iterator
An iterator is an object that defines a sequence and potentially a return value upon completion. It must implement a next()
method that returns an object with value
and done
properties.
Use iterators when:
- You want fine-grained control over iteration.
- You are implementing custom data structures (e.g., trees, graphs) that need to conform to the iterable protocol.
- You want to manually manage the state of iteration.
Example
function createIterator(arr) {
let index = 0;
return {
next: function () {
if (index < arr.length) {
return { value: arr[index++], done: false };
} else {
return { done: true };
}
}
};
}
const it = createIterator([10, 20, 30]);
console.log(it.next()); // { value: 10, done: false }
console.log(it.next()); // { value: 20, done: false }
console.log(it.next()); // { value: 30, done: false }
console.log(it.next()); // { done: true }
⚙️ Generator
A generator is a special function using function*
syntax that can pause and resume execution using the yield
keyword.
Use generators when:
- You need lazy evaluation or infinite sequences.
- You want to create iterators more simply.
- You need to pause and resume logic (like co-routines).
- You’re working with asynchronous flows (using
async generators
withfor await
).
Example
function* numberGenerator() {
yield 10;
yield 20;
yield 30;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 10, done: false }
console.log(gen.next()); // { value: 20, done: false }
console.log(gen.next()); // { value: 30, done: false }
console.log(gen.next()); // { value: undefined, done: true }
🆚 Key Differences
Feature | Iterator | Generator |
---|---|---|
Syntax | Manual object with next() | Uses function* and yield |
Code Complexity | More verbose | Concise and readable |
State Management | Manual | Automatically managed |
Reusability | Custom setup needed | Can be reused easily |
Use in Loops | Harder, manual loop | Works with for...of out of the box |
Lazy Evaluation | Yes, but manually | Yes, naturally supported |
Pause/Resume logic | No | Yes, with yield |
Async Support | No (without Promises) | Yes, via async function* |
Examples
🐍 Python Example
Iterator
class MyIterator:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current >= self.limit:
raise StopIteration
val = self.current
self.current += 1
return val
it = MyIterator(3)
for val in it:
print(val) # 0 1 2
Generator
def my_generator(limit):
for i in range(limit):
yield i
for val in my_generator(3):
print(val) # 0 1 2
🌐 JavaScript Example
Iterator (Manual)
const myIterator = {
current: 0,
limit: 3,
[Symbol.iterator]() {
return {
current: this.current,
limit: this.limit,
next() {
if (this.current < this.limit) {
return { value: this.current++, done: false };
}
return { done: true };
}
};
}
};
for (const item of myIterator) {
console.log(item);
}
Generator
function* myGenerator(limit) {
for (let i = 0; i < limit; i++) {
yield i;
}
}
for (const val of myGenerator(3)) {
console.log(val); // 0 1 2
}
You can also have async generator but you need to await the iterator.
async function* myGenerator(limit) {
for (let i = 0; i < limit; i++) {
yield i;
}
}
for await (const val of myGenerator(3)) {
console.log(val); // 0 1 2
}
✅ Summary
- Use generators when you want a simpler, more powerful way to create iterators or need to pause/resume execution. When performance optimization is needed.
- Use iterators when you want custom control over how iteration works or need to implement the iterator protocol manually.