function* ids() {
let id = 1;
while (true) yield id++;
}
const gen = ids();
gen.next().value; // 1
gen.next().value; // 2
A generator is a function that can pause and resume. You declare it with function*. Calling it does not run the body. It returns an iterator instead.
How it works:
- Each call to
next()runs the body until the nextyield. next()returns{ value, done }.- The function then pauses, and keeps its local variables.
return, or reaching the end, givesdone: true.
Why it is useful:
Generators follow the iterator protocol. This is the standard rule that an object has a next() method returning { value, done }. So generators work with for...of, spread and destructuring.
This makes them a short way to write custom iterables. They are also good for lazy sequences, which compute each value only when asked. A sequence can even be infinite, like an ID generator or paginated data.
Two-way communication:
next(value)sends a value back in. It becomes the result of the pausedyield.throw()sends an error into the generator.
Libraries like redux-saga used this to drive async flows. Older async/await polyfills (code that adds the feature to old engines) did too.
Async generators (async function*) work with for await...of to read streams.
function*returns an iterator.yieldpauses the function, andnext()resumes it.next()returns an object{ value, done }.- Generators work with
for...ofand spread, and suit lazy or infinite sequences. next(value)sends values into the generator. redux-saga is built on this.- Async generators are read with
for await...of.