JavaScript offers several powerful tools for controlling flow, and one of the most flexible—but underutilized—is the Generator Function. Generators allow functions to pause execution and resume later, making them incredibly useful for scenarios like lazy evaluation, streaming data, and asynchronous iteration.
📘 Table of Contents
- What Are Generator Functions?
- Syntax and Basics
- Understanding
yield
andnext()
- Real-World Use Cases
- Use Case 1: Lazy Pagination
- Use Case 2: Infinite Sequence Generator
- Use Case 3: Custom Iterable Objects
- Use Case 4: Controlled Execution for Testing
- Best Practices
- Generator vs Async/Await
- Summary
1. 🧠 What Are Generator Functions?
Generator functions are functions that can be paused and resumed. They return an iterator object that adheres to the Iterator Protocol, which has a .next()
method to retrieve the next value.
2. ✍️ Syntax and Basics
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
Usage:
const gen = myGenerator();
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()); // { value: undefined, done: true }
3. 🛠 How yield
and next()
Work
yield
:
- Pauses the function
- Returns a value
- Saves the state of the function
next()
:
- Resumes the function from the last yield
- Returns
{ value: X, done: false }
until the function ends - At the end:
{ value: undefined, done: true }
4. 🔍 Real-World Use Cases
✅ Use Case 1: Lazy Pagination (Batch Processing)
Ideal for handling large datasets in smaller, manageable chunks.
function* paginate(data, size) {
for (let i = 0; i < data.length; i += size) {
yield data.slice(i, i + size);
}
}
const data = Array.from({ length: 100 }, (_, i) => i + 1);
const paged = paginate(data, 10);
console.log(paged.next().value); // [1...10]
console.log(paged.next().value); // [11...20]
♾️ Use Case 2: Infinite Sequence Generator
Useful in simulations, auto-increment IDs, or streaming tokens.
function* idGenerator() {
let id = 0;
while (true) {
yield id++;
}
}
const ids = idGenerator();
console.log(ids.next().value); // 0
console.log(ids.next().value); // 1
🔁 Use Case 3: Custom Iterable Objects
Generators can make any object iterable using the [Symbol.iterator]
protocol.
const myRange = {
start: 1,
end: 5,
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
};
for (let num of myRange) {
console.log(num); // 1, 2, 3, 4, 5
}
🧪 Use Case 4: Controlled Execution (e.g. for Testing)
Generators allow step-by-step execution, ideal for mocking or testing.
function* testSteps() {
console.log('Step 1');
yield;
console.log('Step 2');
yield;
console.log('Step 3');
}
const test = testSteps();
test.next(); // logs: Step 1
test.next(); // logs: Step 2
test.next(); // logs: Step 3
5. 💡 Best Practices
Tip | Why |
---|---|
Use for...of to consume generators |
Cleaner and more readable |
Avoid infinite loops unless controlled | Can crash the app if not handled properly |
Combine with yield* for delegation |
Great for modular generators |
Don't mutate external state | Keep generators pure for testability |
6. ⚔️ Generator vs Async/Await
Feature | Generator | Async/Await |
---|---|---|
Pause/resume | ✅ Yes | ❌ No |
Async operations | ❌ Not directly | ✅ Yes |
Use case | Streams, lazy data | Async I/O |
Syntax |
yield , next()
|
await , async function
|
You can even create async generators:
async function* fetchPages(urls) {
for (let url of urls) {
const res = await fetch(url);
yield res.json();
}
}
✅ Summary
JavaScript Generators are powerful, lightweight, and elegant tools to handle:
- Custom iteration
- Infinite sequences
- On-demand data processing
- Simulation/test frameworks
Use them when you need controlled, lazy, or infinite data flow—and you’ll write cleaner and more efficient code.
Top comments (0)