W3docs

JavaScript Generator Functions

Learn JavaScript generators: the function* syntax, yield and yield*, sending values with next(), return() and throw(), lazy infinite sequences.

A generator is a special kind of function that can pause itself in the middle, hand a value back to the caller, and then resume exactly where it left off the next time you ask for more. Instead of computing everything up front and returning once, a generator produces a sequence of values lazily — one at a time, only when requested.

That single idea makes generators the cleanest way in JavaScript to build custom iterables, model infinite sequences without running out of memory, and step through long-running logic on demand. This guide covers the function* syntax, yield and yield*, two-way communication with next(), early termination, and the practical patterns where generators shine.

This page builds on iterables and iterators — if the iterator protocol is new to you, read that first.

Understanding Generator Functions

The Basics of Generator Functions

A generator function is declared like a regular function but with a star after the function keyword: function*. The star is what marks the function as a generator.

Calling a generator function does not run its body. Instead it returns a generator object — an iterator you drive manually. Each call to its next() method runs the body up to the next yield, then pauses and returns { value, done }:

javascript— editable

The first next() runs until the first yield 'Hello' and pauses there. The second resumes after that line and runs until yield 'World'. The third resumes, finds no more yield statements, reaches the end of the function, and reports done: true. Calling next() again would keep returning { value: undefined, done: true }.

Because a generator object is also iterable, you can consume it with for...of, the spread operator, or destructuring — all of which stop automatically when done becomes true:

javascript— editable
Note

A generator object can only be iterated once. After it is exhausted, for...of produces nothing. Call the generator function again to get a fresh generator.

A generator's return value

A return inside a generator ends the iteration and supplies the final value together with done: true. Note that for...of ignores this returned value — it only sees the yielded ones:

javascript— editable

Controlling the Flow with return() and throw()

Besides next(), a generator object exposes two more methods that let the caller steer execution from the outside:

  • generator.return(value) forces the generator to finish immediately, running any finally blocks on the way out and returning { value, done: true }.
  • generator.throw(error) injects an exception at the current yield point, so the generator can catch it with try...catch — or propagate it if it doesn't.
javascript— editable

The finally block makes generators a tidy place to release resources (close a file, disconnect a stream) even when iteration is abandoned early.

Advanced Generator Patterns

Delegating with yield*

When one generator needs to produce the values of another generator or any iterable, use yield* (read "yield-delegate") instead of writing a manual loop. It transparently forwards every value from the delegated source:

javascript— editable

A useful detail: the expression value of yield* is the return value of the inner generator, which lets you compose generators and pass a result back to the outer one:

javascript— editable

Sending Values into Generators

Communication with a generator is two-way. A yield expression not only sends a value out through next(), it also evaluates to whatever you pass into the next next(value) call. This turns a generator into a small interactive coroutine:

javascript— editable

The first next() runs up to the first yield and returns the question; its argument is discarded because there is no paused yield to receive it yet. The second next('Alice') resumes the paused yield, so the expression becomes 'Alice' and gets assigned to name.

Practical Applications of JavaScript Generators

Building Custom Iterables

The most common real-world use of generators is to make your own objects iterable. Define [Symbol.iterator] as a generator method, and any for...of, spread, or destructuring will work on the object. This is far less boilerplate than writing an iterator object with a hand-rolled next() by hand (see Symbol type for what Symbol.iterator is):

javascript— editable

Lazy and Infinite Sequences

Because a generator computes each value only when asked, it can describe a sequence that is conceptually infinite without ever exhausting memory. The classic example is a counter, but the same approach builds ranges, ID generators, or Fibonacci streams. You combine it with a "take" helper that stops after the count you need:

javascript— editable

A parameterised range generator is a handy, reusable variant of the same idea:

javascript— editable

Managing Asynchronous Operations

Historically, generators were used to flatten "callback hell": each yield paused on a Promise, and an external runner resumed the generator once that Promise resolved. The example below shows the manual mechanics so you can see what is happening under the hood:

javascript— editable
Note

This is a simplified manual runner. In modern code you would write this with async/await, which the language built directly on top of this generator-plus-Promise pattern. For asynchronous iteration (streaming chunks over time) use async generators with async function* and for await...of.

Generators vs. Regular Functions

AspectRegular functionGenerator function
Declarationfunction fn()function* fn()
Calling itruns the body to completionreturns a generator object, runs nothing yet
Returnsa single valuea stream of values via yield
Can pause?noyes, at every yield
Iterable?noyes (works with for...of, spread)

Reach for a generator when you need values produced lazily or on demand, an object to be iterable, or a sequence that may be infinite. For a plain one-shot computation, a regular function is simpler and faster.

Conclusion

Generators give JavaScript a clean way to produce sequences lazily, pause and resume logic, and make any object work with loops like for...of. Master yield for emitting values, yield* for delegating to other iterables, and next()/return()/throw() for driving and controlling the flow — and you have a tool that powers everything from custom iterables to the async/await syntax built on top of it.

Practice

Practice
What is the function of the '*' character before the function keyword in JavaScript generators?
What is the function of the '*' character before the function keyword in JavaScript generators?
Was this page helpful?