This chapter follows Iterators in JavaScript.
A generator is a function that can pause and resume, passing values in both directions at each pause. Generators are iterators: calling a generator function returns an iterator object with next(), return(), and throw().
Define generators with function* (or method/generator shorthand in objects and classes):
function* myGen(start, end) { for (let n = start; n < end; n++) { yield n; }}const genIterator = myGen(0, 3);console.log(genIterator.next()); // { value: 0, done: false }console.log(genIterator.next()); // { value: 1, done: false }console.log(genIterator.next()); // { value: 2, done: false }console.log(genIterator.next()); // { value: undefined, done: true }Invoking myGen(0, 3) does not run the loop body yet—it returns an iterator. As in the iterators chapter, execution advances only when you call next() (or use for..of).
Each call to a generator function creates a new iterator; a single iterator is consumed once.
Sending values to and from yield
When you call it.next(arg):
- The generator runs until the next
yield. yield exprevaluates to{ value: expr, done: false }for the caller.- The argument to the following
next()becomes the result of that yield expression inside the generator.
function* myGenerator() { const inp = yield "Hi"; console.log(inp); const name = yield; yield "My name is " + name;}
const it = myGenerator();console.log(it.next("A")); // { value: "Hi", done: false }console.log(it.next("B")); // logs "B", then { value: undefined, done: false }console.log(it.next("C")); // { value: "My name is C", done: false }console.log(it.next("D")); // { value: undefined, done: true }Using return(value) inside a generator completes the iterator (done: true) and sets the final value. That is the normal way to finish early with a result.
for..of over a generator
Because a generator return value is an iterable iterator, for..of works directly:
function* myGen() { let x = 0; while (x < 4) { yield x; x++; }}
for (const val of myGen()) { console.log(val);}// 0, 1, 2, 3Yield delegation
yield* delegates iteration to another iterable or generator, forwarding yields until that inner sequence completes. The return value of the inner generator becomes the value of the yield* expression.
function* firstGen() { yield 1; yield 2; return 4;}
function* secondGen() { const firstGenReturnVal = yield* firstGen(); yield 3; yield firstGenReturnVal;}
const it = secondGen();console.log(it.next()); // { value: 1, done: false }console.log(it.next()); // { value: 2, done: false }console.log(it.next()); // { value: 3, done: false }console.log(it.next()); // { value: 4, done: false }console.log(it.next()); // { value: undefined, done: true }yield* also flattens any iterable, including arrays returned from ordinary functions:
function normalFunction() { return ["one", "two", "three"];}
function* gen() { yield* normalFunction();}
for (const v of gen()) { console.log(v);}Early generator completion
Without editing the generator body, the consumer can stop iteration:
iterator.return(value)— finishes the generator at the current suspension pointiterator.throw(error)— injects an error at the suspension point (must be caught inside the generator or the iterator completes)
function* myGen() { let x = 0; while (x < 10) { yield x; x++; }}
const it = myGen();console.log(it.next()); // { value: 0, done: false }console.log(it.return()); // { value: undefined, done: true }console.log(it.next()); // { value: undefined, done: true }function* myGen() { let x = 0; try { while (x < 10) { yield x; x++; } } catch (error) { console.error(error); }}
const it = myGen();console.log(it.next()); // { value: 0, done: false }console.log(it.throw("Oh oh")); // logs error, { value: undefined, done: true }Continue with Async iterators and for await..of.