Suggest an editImprove this articleRefine the answer for “What are generators in JavaScript?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Generators** are JavaScript functions that pause execution at each `yield` and resume when `.next()` is called. ```javascript function* counter() { yield 1; yield 2; yield 3; } const gen = counter(); gen.next(); // { value: 1, done: false } gen.next(); // { value: 2, done: false } ``` **Key:** values are produced lazily, one per `.next()` call, not all at once.Shown above the full answer for quick recall.Answer (EN)Image**Generators** are JavaScript functions that pause mid-execution and resume exactly where they left off, producing values one at a time via the `yield` keyword. ## Theory ### TL;DR - Think of a vending machine: you insert a coin (`.next()`), get one item (`yield` value), and the machine waits for the next coin instead of dispensing everything at once. - Regular functions run to completion and return once. Generators return an iterator object immediately and produce values lazily, one per `.next()` call. - Use generators when processing large datasets or infinite sequences where loading everything into memory at once is not practical (typically 10k+ items). - `function*` and `yield` are not optional syntax sugar. They are what make a function a generator. ### Quick example ```javascript function* numberGen() { yield 1; // pauses here on first .next() yield 2; yield 3; return 4; // final value, then done: true } const gen = numberGen(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: 3, done: false } console.log(gen.next()); // { value: 4, done: true } ``` Each `.next()` resumes from the last `yield`, returns `{ value, done }`, and pauses again. After `return`, every subsequent `.next()` returns `{ value: undefined, done: true }`. ### Key difference from regular functions A regular function runs to completion the moment you call it and returns one value. A generator returns an iterator object immediately, without running any body code. The body starts only on the first `.next()`, then pauses at each `yield`. This is lazy evaluation: values compute only when requested, not upfront. ### When to use generators - **Large or infinite data**: Generate paginated API results, number ranges, or tree traversals on demand instead of building a full array first. - **State machines**: Track multi-step processes like game turns or form wizards without a class. - **Two-way communication**: Pass values back into a running generator via `.next(value)`, used heavily in Redux-Saga. - Skip generators for simple one-shot computations. Regular functions are the right tool there. ### How V8 compiles generators V8 compiles `function*` into a state machine. Each `yield` becomes a state transition point. The generator object holds a pointer to a suspended execution context. When you call `.next(arg)`, the engine resumes from that state, and `arg` becomes the return value of the `yield` expression inside the function. That is why you can send values back in, not just get them out. ### Common mistakes **Expecting the generator to run on creation** ```javascript function* gen() { yield 1; } const g = gen(); // g is an iterator, NOT the value 1 console.log(g); // Generator {} ``` No code runs until you call `.next()`. Creating the generator and running it are two separate steps. **Forgetting to check `done: true`** ```javascript function* gen() { yield 1; } const g = gen(); g.next(); // { value: 1, done: false } g.next(); // { value: undefined, done: true } console.log(g.next().value); // undefined - generator is exhausted ``` After the generator returns, all subsequent `.next()` calls give `{ value: undefined, done: true }`. Use a `while` loop to guard against this: ```javascript let result = g.next(); while (!result.done) { console.log(result.value); result = g.next(); } ``` **Accidentally sharing state across instances** ```javascript let count = 0; function* sharedGen() { yield count++; } const g1 = sharedGen(); g1.next(); // { value: 0, done: false } const g2 = sharedGen(); g2.next(); // { value: 1, done: false } - shared outer count! ``` Each invocation shares the outer closure. Move state inside to get independent generators: ```javascript function* gen() { let count = 0; yield count++; } ``` **Misunderstanding value injection on the first `.next()`** Any argument you pass to the first `.next()` call is silently discarded. No `yield` expression inside the function is waiting to receive it yet. From the second call onwards, the argument to `.next(arg)` becomes the return value of the previous `yield` expression. The Advanced example below shows a full walkthrough. ### Real-world usage - **Redux-Saga**: Effects like `call()` and `takeEvery()` are built on generator pausing. The saga middleware calls `.next()` with resolved values to coordinate async operations. - **Node.js streams**: `readableStream[Symbol.asyncIterator]()` uses async generators for backpressure handling (Node 10+). - **Infinite sequences**: ID generators, number ranges, or test data factories where you don't know upfront how many values you need. - **Paginated DB queries**: Fetch in batches, yielding each page, without loading the full dataset into memory. I used this pattern in a Node.js service processing 500k+ user records. Loading everything at once caused memory spikes. Switching to a generator that fetched batches of 500 kept memory flat for the entire run. ### Follow-up questions **Q:** How do you iterate over a generator? **A:** Three ways: `for...of` (auto-calls `.next()` until `done: true`), spread operator (`[...gen]` converts to array), or a manual `while (!result.done)` loop. Use `for...of` unless you specifically need the result as an array. **Q:** What is `yield*` and how does it differ from `yield`? **A:** `yield` pauses with a single value. `yield*` delegates to another iterable or generator and yields all its values in sequence. It is shorthand for a loop that yields each item from the inner iterable one by one. **Q:** Can a generator handle errors? **A:** Yes. Call `.throw(err)` on the generator to inject an error at the current `yield` point. If the generator wraps that `yield` in `try/catch`, it handles the error and continues. If not, the error propagates to the caller. **Q:** What is the memory benefit of generators over arrays for large sequences? **A:** An array holding 1 million items allocates memory for all of them upfront. A generator produces one value per `.next()`, reusing the same execution context with no extra stack allocation. For infinite sequences, an array is simply not an option. **Q:** How do generators connect to async iteration (senior-level)? **A:** `async function*` combines generators with promises. Each `yield` can await an async operation before producing a value. The caller iterates with `for await...of`. V8 handles this via bytecode suspension, the same mechanism as regular generators but integrated with promise resolution. ## Examples ### Basic: range generator with `for...of` The simplest real use case: generating a number range without building an array. ```javascript function* range(start, end) { for (let i = start; i <= end; i++) { yield i; // pauses after each value } } for (const num of range(1, 5)) { console.log(num); // 1, 2, 3, 4, 5 } // for...of handles .next() and the done check automatically ``` `for...of` calls `.next()` internally and stops when `done: true`. No manual loop boilerplate needed. ### Intermediate: paginated database fetch Fetching users page by page, without loading all records into memory at once. ```javascript function* fetchUsersPaginated(pageSize = 5) { let page = 0; while (true) { const users = fetchUsersFromDB(page, pageSize); // hypothetical DB call if (users.length === 0) return; // stops when no more data yield users; // one page at a time page++; } } const userGen = fetchUsersPaginated(); console.log(userGen.next().value); // [user1, user2, user3, user4, user5] console.log(userGen.next().value); // [user6, user7, user8, user9, user10] // only the current page is in memory at any given moment ``` The generator pauses after each `yield users` and only fetches the next page when you call `.next()` again. Memory usage stays predictable regardless of total record count. ### Advanced: two-way communication via `.next(value)` The pattern that trips up most developers. Values flow both out (via `yield`) and in (via `.next(arg)`). ```javascript function* auctionGen() { const bid = yield 'Waiting for first bid'; // pauses; 'bid' gets the argument of the next .next() const counterBid = yield `Current high: ${bid}`; // pauses again yield `Sold for ${Math.max(bid, counterBid)}`; } const auction = auctionGen(); console.log(auction.next()); // { value: 'Waiting for first bid', done: false } console.log(auction.next(100)); // 100 becomes 'bid' → { value: 'Current high: 100', done: false } console.log(auction.next(150)); // 150 becomes 'counterBid' → { value: 'Sold for 150', done: false } ``` Redux-Saga uses exactly this mechanism. The saga middleware sends resolved promise values back into the generator via `.next()`, letting sagas read async results as synchronous assignments.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.