An ordinary function runs from entry to return, but some tasks need to produce one value, pause, and continue later without rebuilding their state. JavaScript generators provide that control. Generators expose each next() result and can also produce a lazy numeric range.
What a JavaScript generator returns
function* defines a generator function. Calling it returns a generator object without running the body to completion. Each yield pauses the body, and next() resumes it. The returned iterator result has two fields: value is the yielded or returned value, while done says whether the generator has finished.
function* ticketCounter() {
yield "A101";
yield "A102";
return "closed";
}
const counter = ticketCounter();
counter.next(); // { value: "A101", done: false }
counter.next(); // { value: "A102", done: false }
counter.next(); // { value: "closed", done: true }
counter.next(); // { value: undefined, done: true }The first call starts execution and pauses at the first yield. The third call reaches return "closed", so that value appears in the manual result that completes the generator. However, for...of and spread collect yielded values only. Therefore, [...ticketCounter()] is exactly ["A101", "A102"].

Build a lazy stepped range
This generator yields a finite arithmetic sequence. The guard matters because step = 0 would prevent the loop from advancing and create an endless sequence.
function* range(start, end, step = 1) {
if (!Number.isFinite(step) || step <= 0) {
throw new RangeError("step must be a positive number");
}
for (let n = start; n <= end; n += step) {
yield n;
}
}
const values = [...range(3, 11, 2)];
const total = values.reduce((sum, n) => sum + n, 0);Start at 3 and add 2 after every resume: 3, 5, 7, 9, 11. The next call after 11 returns { value: undefined, done: true }. Thus values is [3, 5, 7, 9, 11], and the sum is 3 + 5 + 7 + 9 + 11 = 35. The generator calculates one n per resume instead of constructing the whole array inside range.
Each call creates fresh state. If a and b are both range(3, 11, 2), advancing a twice gives 3 and 5, while the first b.next() still gives 3.
Consume a finite slice safely
A manual loop exposes each { value, done } result. for...of visits 3, 5, 7, 9, 11; spread creates [3, 5, 7, 9, 11]; and const [first, second] = range(3, 11, 2) assigns first = 3 and second = 5.
An unbounded generator needs a bounded consumer:
function* ids(start = 100) {
let id = start;
while (true) yield `ID-${id++}`;
}
function take(iterator, count) {
const output = [];
for (let i = 0; i < count; i += 1) {
const { value, done } = iterator.next();
if (done) break;
output.push(value);
}
return output;
}
take(ids(100), 3); // ["ID-100", "ID-101", "ID-102"]take(..., 3) stops after three values. By contrast, [...ids(100)] never reaches done: true, so it does not finish and must not be used.
Compose sequences with yield*
yield* delegates to another iterable and forwards its values.
function* featuredScores() {
yield* range(1, 5, 2);
yield 99;
}
[...featuredScores()]; // [1, 3, 5, 99]Here, yield* range(1, 5, 2) forwards 1, 3, and 5. Writing yield range(1, 5, 2) would yield one generator object instead. The same rule works with built-in iterables: yield* ["home", "courses"] emits the two strings separately. Use yield* when one generator should expose another iterable's values as part of its own sequence.
Send values back into a paused generator
Generators can receive data as well as produce it:
function* billCalculator() {
const quantity = yield "quantity?";
const unitPrice = yield "unit price?";
return quantity * unitPrice;
}
const bill = billCalculator();
bill.next("ignored"); // { value: "quantity?", done: false }
bill.next(4); // { value: "unit price?", done: false }
bill.next(125); // { value: 500, done: true }The 4 becomes the result of the suspended yield "quantity?", and 125 becomes the result of yield "unit price?". The first next() argument is ignored because execution has not yet paused at a yield expression. Prompts travel out; later next(value) arguments travel back in. The final calculation is 4 * 125 = 500.

Stop early and run cleanup
Calling return() asks a generator to finish immediately. A finally block still runs, which makes it suitable for cleanup.
function* inventory() {
try {
yield "keyboard";
yield "mouse";
} finally {
console.log("inventory closed");
}
}
const items = inventory();
items.next(); // { value: "keyboard", done: false }
items.return("stopped"); // logs "inventory closed"
// returns { value: "stopped", done: true }
items.next(); // { value: undefined, done: true }generator.throw(error) resumes at the paused yield by throwing there, so treat it as recoverable only when the generator has a matching try...catch.
Common errors and generator practice tasks
Mistake | What happens | Fix |
|---|---|---|
Write | Syntax error | Add |
Expect | You get a generator object | Call |
Pass data in the first | The value is lost | Prime the generator before sending |
Expect | The array omits it | Inspect the terminal manual result |
Call | It stays finished | Create a fresh generator object |
Spread | It never finishes | Consume a bounded count |
For practice, first implement an unbounded Fibonacci generator. With state a = 0, b = 1, the first yield is 0, then update to (1, 1); the second yield is 1, then update to (1, 2); the third yield is 1, then update to (2, 3). Your check is take(fibonacci(), 7), which must return [0, 1, 1, 2, 3, 5, 8].
Next, implement rangeDown(10, 1, 3). Its state variable pauses at 10, 7, and 4 for the first three yields, then reaches 1. Spread must return [10, 7, 4, 1].
Useful assessment tasks include predicting a next() trace, repairing a missing function*, writing a bounded lazy sequence, and explaining why spread hangs. Continue with String Handling in Java for another language-state deep dive, or practise more step-by-step code with SQL Queries for Placement Interviews.
JavaScript generators in the short version
function*creates resumable logic.yieldproduces a value and preserves local state.next()returns{ value, done }and can send a value into a pausedyield.yield*delegates to another iterable.
The range 3, 5, 7, 9, 11 shows lazy production, while 4 * 125 = 500 shows two-way communication. Your main next step is the Complete JavaScript Course. Move to the React and Redux Course when you are ready to apply language mechanics in application development, or browse the Coding & Skill Development Courses for a wider path.




