yield*
BaselineWidely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.
Theyield*
operator can be used within generator (sync or async) functions to delegate to anotheriterable object, such as aGenerator
. Inside async generator functions, it can additionally be used to delegate to another async iterable object, such as anAsyncGenerator
.
Try it
function* func1() { yield 42;}function* func2() { yield* func1();}const iterator = func2();console.log(iterator.next().value);// Expected output: 42
Syntax
yield* expression
Parameters
expression
OptionalAn iterable object.
Return value
Returns the value returned by that iterator when it's closed (whendone
istrue
).
Description
Theyield*
expression iterates over the operand and yields each value returned by it. It delegates iteration of the current generator to an underlying iterator — which we will refer to as "generator" and "iterator", respectively.yield*
first gets the iterator from the operand by calling the latter's[Symbol.iterator]()
method. Then, each time thenext()
method of the generator is called,yield*
calls the iterator'snext()
method, passing the argument received by the generator'snext()
method (alwaysundefined
for the first call), and yielding the same result object as what's returned from the iterator'snext()
method. If the iterator result hasdone: true
, then theyield*
expression stops executing and returns thevalue
of that result.
Theyield*
operator forwards the current generator'sthrow()
andreturn()
methods to the underlying iterator as well. If the current generator is prematurely closed through one of these methods, the underlying iterator will be notified. If the generator'sthrow()
/return()
method is called, thethrow()
/return()
method of the underlying iterator is called with the same argument. The return value ofthrow()
/return()
is handled like thenext()
method's result, and if the method throws, the exception is propagated from theyield*
expression.
If the underlying iterator doesn't have areturn()
method, theyield*
expression turns into areturn
statement, just like callingreturn()
on a suspendedyield
expression.
If the underlying iterator doesn't have athrow()
method, this causesyield*
to throw aTypeError
– but before throwing the error, the underlying iterator'sreturn()
method is called if one exists.
Examples
Delegating to another generator
In following code, values yielded byg1()
are returned fromnext()
calls just like those which are yielded byg2()
.
function* g1() { yield 2; yield 3; yield 4;}function* g2() { yield 1; yield* g1(); yield 5;}const gen = g2();console.log(gen.next()); // {value: 1, done: false}console.log(gen.next()); // {value: 2, done: false}console.log(gen.next()); // {value: 3, done: false}console.log(gen.next()); // {value: 4, done: false}console.log(gen.next()); // {value: 5, done: false}console.log(gen.next()); // {value: undefined, done: true}
Other Iterable objects
Besides generator objects,yield*
can alsoyield
other kindsof iterables (e.g., arrays, strings, orarguments
objects).
function* g3(...args) { yield* [1, 2]; yield* "34"; yield* args;}const gen = g3(5, 6);console.log(gen.next()); // {value: 1, done: false}console.log(gen.next()); // {value: 2, done: false}console.log(gen.next()); // {value: "3", done: false}console.log(gen.next()); // {value: "4", done: false}console.log(gen.next()); // {value: 5, done: false}console.log(gen.next()); // {value: 6, done: false}console.log(gen.next()); // {value: undefined, done: true}
The value of yield* expression itself
yield*
is an expression, not a statement, so it evaluates to a value.
function* g4() { yield* [1, 2, 3]; return "foo";}function* g5() { const g4ReturnValue = yield* g4(); console.log(g4ReturnValue); // 'foo' return g4ReturnValue;}const gen = g5();console.log(gen.next()); // {value: 1, done: false}console.log(gen.next()); // {value: 2, done: false}console.log(gen.next()); // {value: 3, done: false} done is false because g5 generator isn't finished, only g4console.log(gen.next()); // {value: 'foo', done: true}
Use with async generators
async function* g1() { await Promise.resolve(0); yield "foo";}function* g2() { yield "bar";}async function* g3() { // Can use yield* on both async and sync iterators yield* g1(); yield* g2();}const gen = g3();console.log(await gen.next()); // {value: "foo", done: false}console.log(await gen.next()); // {value: "bar", done: false}console.log(await gen.next()); // {done: true}
Method forwarding
Thenext()
,throw()
, andreturn()
methods of the current generator are all forwarded to the underlying iterator.
const iterable = { [Symbol.iterator]() { let count = 0; return { next(v) { console.log("next called with", v); count++; return { value: count, done: false }; }, return(v) { console.log("return called with", v); return { value: "iterable return value", done: true }; }, throw(v) { console.log("throw called with", v); return { value: "iterable thrown value", done: true }; }, }; },};function* gf() { yield* iterable; return "gf return value";}const gen = gf();console.log(gen.next(10));// next called with undefined; the argument of the first next() call is always ignored// { value: 1, done: false }console.log(gen.next(20));// next called with 20// { value: 2, done: false }console.log(gen.return(30));// return called with 30// { value: 'iterable return value', done: true }console.log(gen.next(40));// { value: undefined, done: true }; gen is already closedconst gen2 = gf();console.log(gen2.next(10));// next called with undefined// { value: 1, done: false }console.log(gen2.throw(50));// throw called with 50// { value: 'gf return value', done: true }console.log(gen.next(60));// { value: undefined, done: true }; gen is already closed
If thereturn()
/throw()
method of the underlying iterator returnsdone: false
, the current generator continues executing andyield*
continues to delegate to the underlying iterator.
const iterable = { [Symbol.iterator]() { let count = 0; return { next(v) { console.log("next called with", v); count++; return { value: count, done: false }; }, return(v) { console.log("return called with", v); return { value: "iterable return value", done: false }; }, }; },};function* gf() { yield* iterable; return "gf return value";}const gen = gf();console.log(gen.next(10));// next called with undefined// { value: 1, done: false }console.log(gen.return(20));// return called with 20// { value: 'iterable return value', done: false }console.log(gen.next(30));// { value: 2, done: false }; gen is not closed
If the underlying iterator doesn't have athrow()
method and the generator'sthrow()
is called,yield*
throws an error.
const iterable = { [Symbol.iterator]() { let count = 0; return { next(v) { count++; return { value: count, done: false }; }, }; },};function* gf() { yield* iterable; return "gf return value";}const gen = gf();gen.next(); // First next() starts the yield* expressiongen.throw(20); // TypeError: The iterator does not provide a 'throw' method.
Specifications
Specification |
---|
ECMAScript® 2026 Language Specification # sec-generator-function-definitions-runtime-semantics-evaluation |