Skip to main content

Generator functions

A generator function is a function that can pause its own execution and then continue from exactly the same place. It returns a generator object and hands out values one at a time through yield, which makes it a convenient base for iterators, lazy evaluation and asynchronous loops.

Theory

TL;DR

  • The declaration looks like function* name() {}: the asterisk after function makes it a generator.
  • Calling it does not run the body, it returns a generator object (an iterator) with a next() method.
  • yield hands out a value and freezes execution; the next next() resumes from the same line.
  • A generator follows the iterator protocol, so it works in for...of, in spread and in destructuring.
  • next(value) passes a value back into the generator, so the exchange goes both ways.
  • async function* together with for await...of brings the same thing to asynchronous streams (ES2018).

Quick example

javascript
function* gen() { yield 1; yield 2; yield 3; } const iterator = gen(); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: undefined, done: true }

What happens here:

  • the first next() call runs up to the first yield, returns 1 and freezes the function;
  • the following next() resumes execution from the very same place;
  • after the last yield the generator finishes its work and reports done: true.

Syntax and the generator object

A generator function is a function that returns a generator object (an iterator) and uses the yield keyword inside itself to pause execution and return values one by one.

javascript
function* name(args) { // function body }
  • * after function turns the function into a generator;
  • yield is used to hand out values step by step;
  • when called, the function does not run immediately, it returns a generator (an iterator).

Every next() call returns an object shaped like { value, done }, where value is whatever yield produced and done tells you whether the generator has already finished.

Generators are iterators

A generator returns an object that is compatible with the iterator protocol. That means you can pass it straight into for...of:

javascript
function* numbers() { yield 10; yield 20; yield 30; } for (const n of numbers()) { console.log(n); }

Output:

javascript
10 20 30

The same generator also works with the spread operator ([...numbers()]) and with array destructuring.

Lazy evaluation and infinite sequences

Generators can be infinite: they do not build the whole array up front, they produce values on demand. That is lazy evaluation, values are generated only as they are needed.

javascript
function* infiniteCounter() { let i = 1; while (true) { yield i++; } } const counter = infiniteCounter(); console.log(counter.next().value); // 1 console.log(counter.next().value); // 2 console.log(counter.next().value); // 3 // ...and so on forever

The same approach is handy for finite sequences you would rather not materialise into an array:

javascript
function* range(start, end, step = 1) { for (let i = start; i <= end; i += step) { yield i; } } for (const num of range(1, 5)) { console.log(num); }

Output:

javascript
1 2 3 4 5

Two way exchange: next(value) and return

Data can travel not only out of the generator but back into it, through the argument of next(value). That argument becomes the result of the yield expression the generator is currently paused on.

javascript
function* dialog() { const name = yield 'What is your name?'; yield `Hello, ${name}!`; } const it = dialog(); console.log(it.next().value); // "What is your name?" console.log(it.next('Maria').value); // "Hello, Maria!"

The first next() only starts the generator and runs it to the first yield, the second one passes 'Maria' inside as the result of that yield expression.

You can finish a generator early with return:

javascript
function* example() { yield 1; return 'end'; yield 2; // never runs } const it = example(); console.log(it.next()); // { value: 1, done: false } console.log(it.next()); // { value: 'end', done: true }

Async generators and where all of this is used

Async generators (ES2018) let you use await inside iterable streams of data. You walk them with for await...of.

javascript
async function* fetchData() { yield await Promise.resolve('First chunk'); yield await Promise.resolve('Second chunk'); } for await (const part of fetchData()) { console.log(part); }

What generators are actually good for:

Use caseExample
Building iteratorsProducing data element by element
Lazy evaluationNo need to keep the whole array in memory
Step by step executionControlling the flow of execution
Asynchronous pipelinesProcessing data streams without loading memory
Emulating coroutinesThe ability to stop and resume code

A summary of the syntax and the capabilities:

PropertyDescription
DefinitionA function declared with function* that returns an iterator
ReturnsA generator object
Keywordyield
Can pause executionYes
Can take values back inYes
Asynchronous versionasync function*
Main useLazy evaluation, iterators, asynchronous streams

Common mistakes

  • Thinking that calling a generator runs its body. gen() only creates a generator object; not a single line of the body runs until you call next().
  • Confusing yield with return. return finishes the generator: everything after it is never handed out, and the returned value arrives together with done: true.
  • Expecting the first next(value) to pass a value inside. The first call only runs the body up to the first yield, so its argument is ignored.
  • Walking an infinite generator with for...of and no break. The loop will never end, you need an explicit limit.
  • Using for...of instead of for await...of with async function*. You will get promises back rather than the values themselves.
  • Reusing an exhausted generator. Once it reports done: true it does not restart: you have to call the generator function again and get a fresh object.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.