Generators: Pause, Resume & Lazy Evaluation
Yield se ruko, next se jao — aur V8 stack frame heap pe rakh ke slow ho jao. Lekin memory king ho!
A generator function is declared with function* (the asterisk is part of the syntax). Calling it does not execute the body — instead, it returns a Generator object that controls execution.
.next() on that object. This is fundamentally different from regular functions.yield pauses the function and sends a value back to the caller. .next() resumes from the last yield. Each .next() returns { value, done }.
// Basic Generator
function* greet() {
console.log("Started");
yield "Hello";
console.log("Resumed");
yield "World";
console.log("Finished");
}
// Calling greet() does NOT run the body!
const gen = greet();
console.log(gen.next());
// Logs: "Started"
// Returns: { value: "Hello", done: false }
console.log(gen.next());
// Logs: "Resumed"
// Returns: { value: "World", done: false }
console.log(gen.next());
// Logs: "Finished"
// Returns: { value: undefined, done: true }
// Generators are iterable
for (const word of greet()) {
console.log(word); // "Hello", "World"
}
Generator objects are both Iterator and Iterable — they implement [Symbol.iterator] returning this. That's why for...of works with them directly.
yield doesn't just send values OUT — it can receive values IN via .next(value). The value passed to .next() becomes the result of the yield expression on the generator's side.
First .next() always ignores its argument — nothing has been yielded yet to receive it. This trips up many developers.
function* dialog() {
const name = yield "What is your name?";
const food = yield "Hello, " + name + "! Favorite food?";
return name + " loves " + food;
}
const chat = dialog();
// First next() starts the generator, argument is ignored
console.log(chat.next());
// { value: "What is your name?", done: false }
// Pass "Sai" back in — becomes result of first yield
console.log(chat.next("Sai"));
// { value: "Hello, Sai! Favorite food?", done: false }
// Pass "Biryani" back in
console.log(chat.next("Biryani"));
// { value: "Sai loves Biryani", done: true }
.next() is called, this context is copied back to the stack. This heap allocation on every yield is why generators are 3-5x slower than regular function calls.Generators don't compute values until asked. This is lazy evaluation — the opposite of eager computation where everything is calculated upfront.
You can create infinite sequences without infinite loops or memory issues. The generator only computes the next value when .next() is called.
// Infinite sequence of natural numbers
function* naturalNumbers() {
let n = 1;
while (true) { // Infinite loop — but safe!
yield n++;
}
}
// Consuming an infinite generator
const numbers = naturalNumbers();
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// Taking first N items
function take(gen, n) {
const result = [];
for (let i = 0; i < n; i++) {
const item = gen.next();
if (item.done) break;
result.push(item.value);
}
return result;
}
console.log(take(naturalNumbers(), 5)); // [1, 2, 3, 4, 5]
// Lazy Fibonacci
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
console.log(take(fibonacci(), 10)); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
yield* delegates to another iterable or generator. It yields all values from the delegated iterable, then resumes the outer generator.
The return value of yield* is the return value of the inner generator (not a yielded value).
function* inner() {
yield "a";
yield "b";
return "inner-done"; // Return value (not yielded)
}
function* outer() {
yield 1;
// Delegates all yields to inner()
const result = yield* inner();
console.log("Inner returned:", result); // "inner-done"
yield 2;
}
for (const val of outer()) {
console.log(val);
}
// Logs: 1, "a", "b", "Inner returned: inner-done", 2
// Note: "inner-done" is NOT yielded, it is the return value of yield*
yield inner() would yield the generator object itself. yield* inner() iterates through the entire inner generator, yielding each value individually. Think of yield* as "flatten and delegate".Beyond .next(), generators have two powerful control methods:
gen.return(value): Forces the generator to finish immediately. Returns { value, done: true }. Runs finally blocks.
gen.throw(error): Throws an error inside the generator at the current yield point. If caught, the generator can continue.
function* resilient() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log("Cleanup!"); // Always runs on return() or throw()
}
}
const gen = resilient();
console.log(gen.next()); // { value: 1, done: false }
// Force finish
console.log(gen.return("stopped"));
// Logs: "Cleanup!"
// Returns: { value: "stopped", done: true }
// Throwing into a generator
function* catcher() {
try {
yield 1;
} catch (err) {
console.log("Caught:", err.message);
yield "recovered"; // Can continue yielding!
}
yield 2;
}
const c = catcher();
c.next(); // Start
c.throw(new Error("Oops!")); // Throws at yield point
// Logs: "Caught: Oops!"
// Returns: { value: "recovered", done: false }
• Generators were the original way to handle async before async/await
• Libraries like "co" and "redux-saga" used them heavily
• While async/await is now standard for promises, generators are still the best tool for lazy data streams, infinite sequences, and complex state machines
Lo kar liya — Key Points:
- ✅ Generators (function*) return Generator objects that are both iterable and iterator
- ✅ yield pauses the function; .next() resumes it and passes values in/out via yield expressions
- ✅ Generators enable lazy evaluation — infinite sequences consume zero memory until .next() is called
- ✅ yield* delegates to another iterable/generator and captures its return value
- ✅ .return(value) forces the generator to finish and runs finally blocks; .throw(error) injects an error at the yield
- ✅ V8 copies the generator's execution context to the heap on yield, making generators 3-5x slower than regular functions
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login