yield
Pauses a generator, hands a value to the caller, and resumes with whatever next() sends back in.
yield
yield expression
yield* expression- Added in JavaScript ES2015
Parameters
| Parameter | Type | Description |
|---|---|---|
$expressionoptional | any= undefined | For plain yield: the value handed to the consumer as the value property of the result object. Omitting it yields undefined. |
$expression (yield*) | Iterable | AsyncIterable | For yield*: an iterable whose iteration is delegated to. Every value it produces is yielded in turn; its return value becomes the value of the yield* expression itself. Async generators may delegate to sync or async iterables. |
Return value
anyThe value passed to the generator's next() method when execution resumes. For yield*, the value of the delegated iterator's final result object — the one carrying done: true.
Errors
SyntaxError if yield appears outside a generator body or inside a plain nested function within one; TypeError if the generator is re-entered while it is already running, or if yield* delegates to a non-iterable value.
Changelog
- PHP ES2018Async generators (async function*) added, so yield and yield* work inside them and yield* may delegate to an async iterable.
- PHP ES2015Introduced with generator functions; yield* delegation was part of the same proposal.
yield is the pause button of a generator function. Execution stops on the spot, the operand is handed out as { value, done: false }, and the function's whole local state — variables, loop counters, the position inside a try block — stays frozen until someone calls next() again.
Reach for it when
You want to produce a sequence without materialising it: an infinite counter, a paged API walker, a parser that emits tokens as it scans. Nothing runs until a consumer pulls, so for...of, spread, and destructuring drive the work on demand.
It reads as well as writes
yield is an expression. Its value is whatever the caller passes to the following next(v), which makes a generator a two-way channel. The pairing is off by one: the argument to the very first next() is discarded, because no yield is suspended yet.
yield* delegates
yield* iterable forwards every value of another iterable through the current generator, so recursive walks stay flat instead of nesting for (const x of inner()) yield x;. It also forwards next() arguments inward and routes return() and throw() to the inner iterator, so finally blocks down the chain still run. Unlike a bare yield, its own value is the inner generator's return value, not a yielded one.
Example
When to use it
- Emit an infinite stream of IDs or timestamps that a consumer pulls from as needed.
- Walk a nested tree or linked list recursively with yield* without building an intermediate array.
- Page through a paginated API, yielding each record while fetching the next page lazily.
- Drive a state machine where each next(value) feeds the next input back into the generator.
Frequently asked questions
What is the difference between yield and yield* in JavaScript?
yield emits one value; yield* delegates to an entire iterable and emits all of its values. yield [1,2,3] produces a single array, while yield* [1,2,3] produces three separate values. yield* also evaluates to the inner generator's return value, and forwards next(), return() and throw() to it.
Why does yield inside forEach throw a SyntaxError?
Because yield is only valid in the body of the generator function that lexically contains it, and the callback you pass to forEach is a separate function. Replace the callback with a for...of loop, or use yield* to delegate to the collection directly.
How do I pass a value into a generator with yield?
Call next(value) — that value becomes the result of the currently suspended yield expression. The argument to the first next() is ignored because no yield is suspended yet, so start a two-way generator with a priming next() call before sending real input.
More examples
yield* for a recursive tree walk
Prints ["a", "c", "d"]. Each recursive call's values flow straight out through yield* with no intermediate arrays, while the return value (the leaf count) is collected by the parent — showing the two channels yield* carries at once. Spreading only ever sees yielded values, never the returned 3.
function* leaves(node) {
if (!node.children) {
yield node.name;
return 1; // becomes the value of `yield* leaves(...)`
}
let count = 0;
for (const child of node.children) count += yield* leaves(child);
return count;
}
const tree = { name: "root", children: [
{ name: "a" },
{ name: "b", children: [{ name: "c" }, { name: "d" }] },
]};
console.log([...leaves(tree)]);Delegating to any iterable, and forwarding return()
Prints [1, 2, "a", "b", 9], then "a", then "inner cleanup", then { value: "stop", done: true }. The cleanup line proves return() called on the outer generator is forwarded to the delegated one, so its finally block runs before the outer generator closes.
function* mix() {
yield* [1, 2]; // array
yield* "ab"; // string, per Unicode code point
yield* new Set([9]); // any iterable works
}
console.log([...mix()]);
function* inner() {
try { yield "a"; yield "b"; } finally { console.log("inner cleanup"); }
}
function* outer() { yield* inner(); }
const g = outer();
console.log(g.next().value);
console.log(g.return("stop"));yield inside an async generator
Prints 1, then 2, then 3. In an async generator each yielded value is delivered as a resolved promise, so the consumer must use for await...of; yield* transparently awaits the delegated async iterator on the way through.
async function* nums() {
yield 1;
yield await Promise.resolve(2); // await and yield mix freely
}
async function* wrap() {
yield* nums(); // delegate to an async iterable
yield 3;
}
for await (const v of wrap()) console.log(v);
Discussion