function*
Define a function that returns a generator, pausing and resuming its body at each yield.
function* name(param0, param1, /* …, */ paramN) {
statements
}- Added in JavaScript ES2015 (ES6)
Parameters
| Parameter | Type | Description |
|---|---|---|
$name | Identifier | The function name. Required in a declaration; optional in a function* expression. |
$paramoptional | any | Formal parameters, exactly as for a normal function — rest and default parameters are allowed. |
$statementsoptional | Statement[] | The function body, which may contain yield and yield* expressions to suspend execution and produce values. |
Return value
GeneratorCalling the function returns a new Generator object — an iterator that also implements the iterable protocol. The function body does not run until the generator's next() method is called.
Errors
Throws a TypeError if a generator function is called with new, since generators are not constructible; a SyntaxError if yield appears outside a generator body.
Changelog
- PHP ES2018async function* added, producing async generators consumed with for await...of.
- PHP ES2025Generator objects inherit the Iterator helper methods (map, filter, take, drop, …) via Iterator.prototype.
A generator function is a function that can pause. yield suspends the body and hands a value out; the next next() call resumes exactly where it stopped, with every local variable intact. Calling the function runs none of the body — it just builds the generator object.
When to reach for it
- Producing a sequence that is infinite, expensive, or of unknown length.
- Making a class or object iterable: define
*[Symbol.iterator]()andfor...ofworks. - Walking a tree or a linked structure, where
yield*makes recursion read almost like the flat version. - Keeping state across calls without a closure full of counters.
What trips people up
Nothing runs until you pull. A generator whose body has a console.log at the top logs nothing when called — a common source of "my validation never ran" bugs. Arrow functions cannot be generators, and new gen() throws a TypeError.
Generators are single-use: once done they stay done, and calling the function again gives you a fresh, independent one. Inside yield*, delegation forwards next(), throw() and return() to the inner iterator, and the value of the yield* expression is the inner generator's return value, not its last yielded one.
Example
When to use it
- Generate an endless stream of unique IDs or a numeric series
- Make a custom collection iterable by defining *[Symbol.iterator]()
- Flatten a tree into a lazy sequence of nodes with yield*
Frequently asked questions
What is the difference between a generator function and a normal function?
A generator function can pause. Calling it returns a Generator object without executing any of the body; each next() runs until the next yield and suspends there, preserving all local state. A normal function runs to completion on every call and returns a single value.
Can an arrow function be a generator?
No. There is no async or sync arrow generator syntax — you must use function*, a method shorthand such as *name() {}, or async function* for async generators. Arrow functions also have no arguments object or own this, which does not compose with generator semantics.
Why does my generator's code not run when I call it?
Because calling a generator function only creates the generator; the body, including any argument validation at the top, runs on the first next() call. If you need eager validation, wrap the generator in a normal function that checks arguments and then returns the generator.
More examples
An infinite series, safely consumed
The infinite while(true) loop is harmless because take() stops pulling. range() shows the finite case: the generator ends naturally, so spreading it terminates.
function* naturals(start = 1) {
let n = start;
while (true) yield n++;
}
// Safe: take() bounds the endless loop.
console.log(naturals().take(5).toArray()); // [1, 2, 3, 4, 5]
function* range(start, end, step = 1) {
for (let i = start; i < end; i += step) yield i;
}
console.log([...range(0, 10, 3)]); // [0, 3, 6, 9]Recursive walking with yield*
yield* splices every value from the recursive call into the outer sequence, so a depth-first traversal comes out flat without any accumulator array.
function* walk(node) {
yield node.value;
for (const child of node.children ?? []) {
yield* walk(child); // delegate to the inner generator
}
}
const tree = {
value: 1,
children: [{ value: 2, children: [{ value: 3 }] }, { value: 4 }],
};
console.log([...walk(tree)]); // [1, 2, 3, 4]
Discussion