JavaScript Generators

JavaScript generators are resumable functions that can pause at `yield` and later continue from the same execution point. They implement the iterator protocol, making them useful for producing values incrementally and controlling iteration explicitly.

A generator function is declared with `function*` and returns a generator object when called. The function body does not execute immediately; execution begins when the generator's `next()` method is called.

Each `yield` pauses execution and produces an iterator result such as `{ value: 10, done: false }`. A later `next()` resumes execution immediately after that `yield`, preserving local variables and control flow between calls.

Generators implement the iterator protocol: they provide a `next()` method that returns objects containing `value` and `done`. Because generators are iterable, they can also be consumed with `for...of`, spread syntax, and other APIs that accept iterables.

The value passed to `next(x)` becomes the result of the currently suspended `yield` expression. The first `next()` argument is effectively ignored because there is no suspended `yield` before the generator starts.

Calling `return()` terminates a generator and supplies its result value, while `throw()` injects an exception at the suspended point. These operations allow consumers to influence generator control flow, not merely retrieve its next value.

Key points

Interview questions

What is a JavaScript generator, and how does `yield` work?

A generator is a special function declared with `function*` that can pause and resume execution. Calling it returns a generator object rather than immediately executing the function body. When `next()` is called, execution continues until the next `yield`, whose value becomes the iterator result's `value`. The generator resumes after that `yield` on the next `next()` call.

How do generators implement the iterator protocol?

A generator object exposes `next()`, which returns an object with `value` and `done` properties. This satisfies the iterator protocol. Generator objects are also iterable, so their `Symbol.iterator` behavior allows them to be consumed by constructs such as `for...of`. This makes generators a convenient mechanism for creating custom lazy sequences.

What does passing an argument to `next()` do?

The argument supplied to `next(value)` becomes the result of the currently suspended `yield` expression. For example, if execution is paused at `const x = yield 1`, calling `next(42)` makes `x` equal to `42`. The first `next()` call cannot provide a value to a `yield` because the generator has not reached a suspended yield yet. This mechanism allows two-way communication between the consumer and generator.

When would you use a generator in frontend JavaScript?

Generators are useful when a computation naturally produces a sequence incrementally or needs explicit pause-and-resume control. They can represent lazy sequences, custom traversal logic, and stateful iteration without manually maintaining an external cursor. They can also model complex synchronous control flow, although modern frontend code commonly uses promises and `async`/`await` for asynchronous operations. The choice should be based on the iteration or control-flow requirement rather than using generators merely because they are available.

What are the effects of `return()` and `throw()` on a generator?

`return(value)` requests termination of the generator and causes a completed iterator result containing that value. `throw(error)` injects an exception at the generator's suspended location. The generator can catch that exception with `try...catch`; otherwise the exception propagates to the caller. These methods provide explicit control over a generator beyond simply requesting its next yielded value.