What are generator functions?
Generator functions are like a playlist you can play one song at a time. You don’t have to load every track into memory. You just press “next,” and the next song appears. In JavaScript, that “next” button is the next() call, and the pause button is yield.
They’re still functions, but with a superpower: they can pause and resume. Instead of returning one big result, they return a stream of results, one step at a time. That’s the whole idea.
Why they exist
Normal functions run start to finish in one go. That’s fine until you need:
- A long sequence you don’t want to store all at once
- A process you want to stop and continue later
- A clean way to express state without messy variables everywhere
Generators were created to make those problems feel simple.
The simple mental model
Calling a generator function doesn’t run it right away. It returns an iterator—an object you can pull values from.
Each call to next():
- Moves the function forward
- Stops at the next
yield - Gives you a
{ value, done }pair
So you’re in control. You decide when the function runs, step by step.
yield is a pause point
Think of yield as a marker that says: “Stop here, give this value back, and wait for me to continue.”
When you call next() again, the function picks up right where it left off.
This pause-and-continue flow is what makes generators special.
It’s lazy by design
Generators don’t do work until you ask for it. That means:
- Less memory use
- Less wasted computation
- The ability to represent huge (or infinite) sequences
yield can also receive values
There’s a neat detail many people miss: yield doesn’t only send values out. It can also receive values in.
When you call next(value), that value becomes the result of the last yield inside the generator. So it’s a two-way conversation:
- The generator sends a value outward.
- The caller can send a value back in.
That makes generators great for step-by-step workflows and configurable pipelines.
Generators vs async functions
It’s easy to mix these up, so here’s a simple way to think about it:
- Generators pause because you call
next(). - Async functions pause because the promise isn’t ready yet.
They solve different problems. If you need lazy, controlled iteration, use generators. If you need to wait for network or I/O, use async.
There’s also async generators, which combine both ideas, but it’s good to learn the basic model first.
Where generators shine
Generators feel natural when you want:
- A lazy pipeline (map/filter without extra arrays)
- Custom iteration (tree traversal, graph walks)
- Simple state machines (steps that must run in order)
- Streaming data (process as it arrives)
If you’ve ever written a loop with a pile of flags and counters, a generator can often make it cleaner.
When to avoid them
If a simple array or loop already works and is easy to read, don’t force a generator. They’re a tool for special cases—mainly when you want control over time or memory.
Common misconceptions
-
“Generators are only about performance.”
Performance is a bonus, but the bigger win is clarity for step-based logic. -
“They’re the same as async.”
They’re different. Generators are about manual control, async is about waiting. -
“They’re too advanced.”
The idea is simple: pause, return a value, resume later.
Closing thoughts
Generator functions give JavaScript a “pause and resume” button. If you ever wish you could run a function one step at a time—like flipping pages in a book—generators are exactly that. Once you see them this way, they’re not scary. They’re just a cleaner way to control flow.