Q31 of 40 · JavaScript
What are generator functions in JavaScript, and when are they useful?
Short answer
Short answer: Generator functions (`function*`) return an iterator that can pause at `yield` expressions and resume on `.next()`. They produce values lazily — useful for infinite sequences, custom iterables, async control flow (before async/await), and test data factories that generate unique values on demand.
Detail
A generator function is declared with function* and uses yield to produce values one at a time. The function body is not executed until .next() is called on the returned iterator.
How it works: Each .next() call resumes execution from the last yield until the next yield or return. The return value of .next() is { value, done }. When the function returns (or falls off the end), done becomes true.
Lazy evaluation: Generators compute values only when requested. A generator can produce an infinite sequence without exhausting memory — values are produced on demand.
Pass values in: .next(value) resumes the generator and the yield expression evaluates to value. This bidirectional communication was used by co and redux-saga before async/await.
as Iterables: Generator functions implement the iterable protocol — you can spread them, use for...of, and destructure them.
In testing: Generators are useful for creating unique ID factories, sequential test data, and lazy fixture sequences.
// EXAMPLE
// Basic generator
function* count(start = 1) {
while (true) {
yield start++;
}
}
const counter = count();
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
// Finite sequence — for...of
function* range(start, end) {
for (let i = start; i <= end; i++) yield i;
}
console.log([...range(1, 5)]); // [1, 2, 3, 4, 5]
// Test ID factory
function* idGenerator(prefix = "user") {
let n = 1;
while (true) yield `${prefix}-${n++}`;
}
const nextId = idGenerator("order");
const id1 = nextId.next().value; // "order-1"
const id2 = nextId.next().value; // "order-2"