Generators and Iterators
Learn the iterator and iterable protocols that power for...of loops, and how generator functions with function* and yield let you build custom, pausable sequences with ease.
Reading Time
20 min
Lesson
Lesson 42 of 48
The Iterator Protocol
The iterator protocol is a simple contract: an object is an iterator if it has a next() method, and calling that method returns an object shaped like { value, done }. value is the next item in the sequence, and done is a boolean that becomes true once there's nothing left to produce. That's the entire contract — no special class or built-in type is required, just an object with the right method shape.
A Manual Iterator
Console Output
Click “Run” to see the console output here.
The Iterable Protocol
The iterable protocol builds on top of the iterator protocol: an object is iterable if it has a method at the special key Symbol.iterator, and calling that method returns an iterator (an object matching the shape above). Being iterable is what makes an object usable with for...of, the spread operator (...), destructuring, and Array.from().
What's Iterable Out of the Box
Console Output
Click “Run” to see the console output here.
Making Your Own Object Iterable
You can make any object work with for...of by giving it a Symbol.iterator method that returns an iterator. This is exactly how you would implement a custom collection type — a linked list, a range, or a queue — that you want to feel like a first-class part of the language.
A Custom Iterable Object
Console Output
Click “Run” to see the console output here.
Generator Functions
Writing the iterator protocol by hand every time is tedious. A generator function — declared with function* and using yield inside — does the same job with far less boilerplate. Calling a generator function does not run its body immediately; instead it returns a generator object, which is both an iterator and an iterable. Each call to .next() runs the function until it hits a yield, pauses there, and returns that yielded value.
A Basic Generator
Console Output
Click “Run” to see the console output here.
Pausing and Resuming Execution
The defining feature of a generator is that execution truly pauses at each yield and picks back up exactly where it left off on the next call to .next() — including any local variables and loop state. This makes generators well suited for representing sequences that are computed lazily, one step at a time, rather than all at once.
Execution Order with Multiple Yields
Console Output
Click “Run” to see the console output here.
Passing Values Into a Generator
yield is a two-way street: not only does it produce a value out of the generator, but whatever you pass to the next .next() call becomes the result of that yield expression inside the generator body. This lets a generator receive input from the outside world at each pause point, not just emit values.
Sending Values Back Into a Generator
Console Output
Click “Run” to see the console output here.
Practical Example: An Infinite Sequence
Generators are a natural fit for infinite sequences, since values are only computed as they are requested. You can safely define a generator that never runs out, and simply stop pulling values from it whenever you have enough.
An Infinite Generator with a Manual Stopping Point
Console Output
Click “Run” to see the console output here.
Iterator vs. Iterable vs. Generator
| Concept | Definition |
|---|---|
| Iterator | An object with a next() method returning { value, done } |
| Iterable | An object with a Symbol.iterator method that returns an iterator |
| Generator | A function* whose calls return an object that is both an iterator and an iterable |
Generators Simplify Custom Iterables
If you find yourself hand-writing a Symbol.iterator method with a manual next() implementation, consider replacing it with a generator function instead — the same for...of behavior, expressed with a plain loop and yield, is usually far easier to read and maintain.
Be Careful with Infinite Generators
An infinite generator used directly in a for...of loop without a break, or spread with the ... operator, or converted with Array.from(), will never finish and will hang or exhaust memory. Always pair an infinite generator with an explicit stopping condition.