yield*
Baseline Widely 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.
In this article
Try it
function* func1() { yield 42;}function* func2() { yield* func1();}const iterator = func2();console.log(iterator.next().value);// Expected output: 42Syntax
yield* expressionParameters
expressionOptionalAn 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, orargumentsobjects).
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 closedIf 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 closedIf 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> |