JavaScript generators aren’t just about iteration; they can tame infinite loops and streams of data without blowing up your memory or CPU.

Introduction
Every developer has faced this dilemma:
- You want to generate an endless sequence (like IDs, timestamps, or random numbers).
- But a naive infinite loop either crashes your app or locks up the browser.
- You try to hack it with counters, arrays, or async intervals, and it gets messy.
👉 Enter JavaScript generators.
Generators let you pause and resume functions, which means you can write infinite loops that behave gracefully: no crashes, no runaway memory, and no wasted CPU cycles.
In this post, we’ll cover:
- ✅ What generators are and how they work
- ✅ How to write infinite loops with generators safely
- ✅ Real-world use cases (IDs, streams, pagination, async pipelines)
- ✅ Common mistakes and gotchas
- ✅ Pro tips for mixing generators with async/await
What Are Generators in JavaScript?
A generator function looks like a normal function, but uses an asterisk (function*
) and the yield
keyword.
Example:
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const gen = numbers();
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 }
- Each
yield
pauses the function. .next()
Resumes it.- You don’t get everything at once; you pull values on demand.
The Infinite Loop Problem
Normally, an infinite loop like this would lock your app:
while (true) {
console.log("Hello, world");
}
This never stops. Your browser freezes. Your app dies.
The Generator Solution
With a generator, you can write an infinite loop that only produces values when asked:
function* infiniteCounter() {
let i = 0;
while (true) {
yield i++;
}
}
const counter = infiniteCounter();
console.log(counter.next().value); // 0
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
// ... and so on
✅ No crash. ✅ Infinite potential values. ✅ Controlled execution.
Real-World Use Cases
1. Unique IDs Without Global State
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const getId = idGenerator();
console.log(getId.next().value); // 1
console.log(getId.next().value); // 2
console.log(getId.next().value); // 3
Instead of let currentId = 0;
You get a clean, self-contained generator.
2. Paginating API Results
function* paginate(api, pageSize = 10) {
let page = 1;
while (true) {
const results = api(page, pageSize);
if (!results.length) return; // stop
yield results;
page++;
}
}
// Usage
for (const page of paginate(fetchUsers, 10)) {
console.log(page);
}
You can fetch “infinite” pages without knowing how many exist.
3. Streaming Data
Generators pair perfectly with async iteration for streams:
async function* messageStream(socket) {
for await (const msg of socket) {
yield msg;
}
}
(async () => {
for await (const message of messageStream(mySocket)) {
console.log("New message:", message);
}
})();
This is infinite message handling without blowing up memory.
4. Random Number Generators
function* randomNumbers() {
while (true) {
yield Math.random();
}
}
const rng = randomNumbers();
console.log(rng.next().value); // random value
console.log(rng.next().value); // another random value
Great for simulations, testing, or procedural generation.
Gotchas with Generators
- Not automatic: A generator won’t run by itself. You need
.next()
orfor..of
. - No memory leaks by default, but if you have
yield
huge objects, you can still bloat memory. - Be careful with async: Use
async function*
for streams, otherwise you’ll get sync values only. - They don’t replace all loops: Sometimes a plain loop is simpler.
Pro Tips
- Combine with Iterators
Generators are iterators that you can plug directly intofor..of
,map
,reduce
, etc. - Lazy Evaluation = Performance Win
Instead of computing everything up front, you only compute what you need when you need it. - Stop with
return
You can break infinite generators gracefully with areturn
. - Mix with Async/Await
async function*
Let’s you create async streams (perfect for APIs, sockets, or files).
Conclusion
Infinite loops in JavaScript don’t have to mean frozen browsers or stack overflows. With generators, you can:
- Build infinite sequences safely
- Handle streams and pagination elegantly
- Write code that’s lazy, efficient, and memory-friendly
✅ Key takeaway: Generators make infinite loops not only possible but practical in modern JavaScript.
Call to Action
Have you ever used generators beyond the basic “yield” demo?
Drop your experience in the comments 👇
And if you know a teammate who still avoids generators because they “look weird,” share this post. It might change how they code forever.
Leave a Reply