[{"data":1,"prerenderedAt":174},["ShallowReactive",2],{"qa-\u002Fjavascript\u002Ffunctions\u002Fgenerators-iterators":3},{"page":4,"siblings":154,"blog":171},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":145,"seo":146,"seoDescription":147,"stem":148,"subtopic":149,"topic":150,"topicSlug":151,"updated":152,"__hash__":153},"qa\u002Fjavascript\u002Ffunctions\u002Fgenerators-iterators.md","Generators Iterators",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","JavaScript","javascript",{},true,5,"\u002Fjavascript\u002Ffunctions\u002Fgenerators-iterators",[23,27,31,36,40,44,48,52,56,60,64,68,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141],{"id":24,"difficulty":14,"q":25,"a":26},"iterator-protocol","What is the iterator protocol?","An **iterator** is any object with a **`next()`** method that returns\n`{ value, done }` on each call — `done: true` signals the end.\n\n```js\nconst it = {\n  i: 0,\n  next() {\n    return this.i \u003C 3\n      ? { value: this.i++, done: false }\n      : { value: undefined, done: true }   \u002F\u002F terminator\n  }\n}\nit.next()  \u002F\u002F { value: 0, done: false }\n```\n\nAnything implementing this shape can be driven manually, but usually you\nconsume iterators through `for...of`, spread, or destructuring.\n",{"id":28,"difficulty":14,"q":29,"a":30},"iterable-protocol","What is the iterable protocol and how does it relate to iterators?","An **iterable** is an object with a **`[Symbol.iterator]()`** method that\n*returns an iterator*. The iterable is the collection; the iterator is the\ncursor over it.\n\n```js\nconst range = {\n  [Symbol.iterator]() {\n    let n = 0\n    return { next: () => n \u003C 3\n      ? { value: n++, done: false }\n      : { value: undefined, done: true } }\n  }\n}\n[...range]   \u002F\u002F [0, 1, 2]  now spreadable \u002F for-of-able\n```\n\n`for...of`, spread, `Array.from`, and destructuring all call\n`Symbol.iterator` under the hood.\n",{"id":32,"difficulty":33,"q":34,"a":35},"builtin-iterables","easy","Which built-in types are iterable?","Arrays, strings, `Map`, `Set`, `arguments`, `TypedArray`, and DOM\ncollections like `NodeList` — all ship a `Symbol.iterator`.\n\n```js\nfor (const ch of 'hi') {}        \u002F\u002F strings\nfor (const [k, v] of map) {}     \u002F\u002F Map yields [key, value]\n```\n\nNotably **plain objects are NOT iterable** — `for...of {}` throws. Use\n`Object.keys\u002Fvalues\u002Fentries`, which return iterable arrays.\n",{"id":37,"difficulty":33,"q":38,"a":39},"generator-basics","What is a generator function?","A **generator** (`function*`) is a function that can **pause and resume**.\nCalling it doesn't run the body — it returns a **generator object** (which\nis both an iterator and iterable). Each `next()` runs to the next `yield`.\n\n```js\nfunction* gen() {\n  yield 1\n  yield 2\n}\nconst g = gen()\ng.next()  \u002F\u002F { value: 1, done: false }\ng.next()  \u002F\u002F { value: 2, done: false }\ng.next()  \u002F\u002F { value: undefined, done: true }\n```\n\nGenerators are the easiest way to build custom iterators without writing\n`next()` by hand.\n",{"id":41,"difficulty":33,"q":42,"a":43},"yield-keyword","What does the yield keyword do?","**`yield`** pauses the generator and emits a value to the caller. Execution\nfreezes — local variables and position are preserved — until the next\n`next()` resumes it right after that `yield`.\n\n```js\nfunction* counter() {\n  let n = 0\n  while (true) yield n++   \u002F\u002F resumes here each time\n}\n```\n\nThis pause\u002Fresume is what makes lazy and infinite sequences possible.\n",{"id":45,"difficulty":14,"q":46,"a":47},"generator-is-iterable","Why can you use a generator directly in for...of and spread?","A generator object implements **both** protocols: it has `next()` (it's an\niterator) and a `Symbol.iterator` that returns itself (it's iterable).\n\n```js\nfunction* g() { yield 'a'; yield 'b' }\n[...g()]                 \u002F\u002F ['a', 'b']\nfor (const x of g()) {}  \u002F\u002F works directly\n```\n\nBecause it returns *itself* from `Symbol.iterator`, a generator is a\n**one-shot** iterable — once consumed it's exhausted.\n",{"id":49,"difficulty":14,"q":50,"a":51},"custom-iterable-with-generator","How do generators simplify making a class iterable?","Define `[Symbol.iterator]` as a **generator method**; `yield` the elements\nand the protocol bookkeeping is handled for you.\n\n```js\nclass Range {\n  constructor(s, e) { this.s = s; this.e = e }\n  *[Symbol.iterator]() {\n    for (let i = this.s; i \u003C this.e; i++) yield i  \u002F\u002F\n  }\n}\n[...new Range(1, 4)]   \u002F\u002F [1, 2, 3]\n```\n\nCompare to the manual `next()`\u002F`done` version — the generator removes all\nthe state plumbing.\n",{"id":53,"difficulty":14,"q":54,"a":55},"lazy-evaluation","What does it mean that generators are lazy?","Values are produced **on demand**, only when `next()` asks. Nothing is\ncomputed until consumed, and computation stops as soon as the consumer\nstops.\n\n```js\nfunction* squares() { let n = 1; while (true) yield n * n++ }\nconst it = squares()\nit.next().value  \u002F\u002F 1   — only this much computed\nit.next().value  \u002F\u002F 4\n```\n\nThis lets you model infinite or expensive sequences and pull just the\nfirst few elements without computing the rest.\n",{"id":57,"difficulty":14,"q":58,"a":59},"infinite-sequence","How do you build an infinite sequence safely?","A generator with an unbounded loop is fine **as long as the consumer\nbounds it**.\n\n```js\nfunction* naturals() { let n = 1; while (true) yield n++ }\nfunction take(it, k) {\n  const out = []\n  for (const x of it) { out.push(x); if (out.length === k) break }\n  return out\n}\ntake(naturals(), 3)   \u002F\u002F [1, 2, 3]\n```\n\nThe pitfall: `[...naturals()]` or `for...of` without a `break` will loop\nforever. Always cap consumption.\n",{"id":61,"difficulty":14,"q":62,"a":63},"yield-delegation","What does yield* (delegation) do?","**`yield*`** delegates to another iterable\u002Fgenerator — it yields *all* of\nits values in place, as if inlined.\n\n```js\nfunction* inner() { yield 1; yield 2 }\nfunction* outer() {\n  yield 0\n  yield* inner()   \u002F\u002F yields 1 then 2\n  yield 3\n}\n[...outer()]   \u002F\u002F [0, 1, 2, 3]\n```\n\nIt also forwards the delegated generator's **return value** as the result\nof the `yield*` expression, useful for composing generators.\n",{"id":65,"difficulty":14,"q":66,"a":67},"yield-delegation-any-iterable","Can yield* delegate to non-generator iterables?","Yes — `yield*` works on any iterable, so you can flatten arrays, strings,\nSets, etc.\n\n```js\nfunction* flatten(arrs) {\n  for (const a of arrs) yield* a   \u002F\u002F spread each array's items\n}\n[...flatten([[1, 2], [3], [4, 5]])]  \u002F\u002F [1, 2, 3, 4, 5]\n```\n\nThis makes recursive flattening of nested structures elegant — just\n`yield* flatten(child)` for sub-arrays.\n",{"id":69,"difficulty":70,"q":71,"a":72},"passing-values-into-next","hard","How do you pass a value back into a generator?","The argument to **`next(value)`** becomes the *result* of the `yield`\nexpression that the generator is paused on — two-way communication.\n\n```js\nfunction* convo() {\n  const name = yield 'What is your name?'\n  yield `Hello, ${name}!`\n}\nconst c = convo()\nc.next().value        \u002F\u002F 'What is your name?'\nc.next('Ada').value   \u002F\u002F 'Hello, Ada!'  'Ada' became `name`\n```\n\nGotcha: the **first** `next()` argument is ignored — there's no paused\n`yield` yet to receive it.\n",{"id":74,"difficulty":70,"q":75,"a":76},"generator-return-method","What does the generator's return() method do?","`gen.return(v)` forces the generator to finish early, yielding\n`{ value: v, done: true }` and running any `finally` blocks for cleanup.\n\n```js\nfunction* g() {\n  try { yield 1; yield 2 }\n  finally { console.log('cleanup') }  \u002F\u002F runs on early return\n}\nconst it = g()\nit.next()        \u002F\u002F { value: 1, done: false }\nit.return(99)    \u002F\u002F logs 'cleanup'; { value: 99, done: true }\n```\n\n`for...of` calls `return()` automatically when you `break`, so resources\nget released.\n",{"id":78,"difficulty":70,"q":79,"a":80},"generator-throw-method","What does the generator's throw() method do?","`gen.throw(err)` injects an exception **at the paused `yield`**, as if the\n`yield` itself threw — the generator can `try\u002Fcatch` it.\n\n```js\nfunction* g() {\n  try { yield 1 }\n  catch (e) { yield `caught ${e}` }  \u002F\u002F\n}\nconst it = g()\nit.next()          \u002F\u002F { value: 1 }\nit.throw('boom')   \u002F\u002F { value: 'caught boom', done: false }\n```\n\nIf the generator doesn't catch it, the error propagates to the caller of\n`throw()`.\n",{"id":82,"difficulty":14,"q":83,"a":84},"return-value-in-generator","What happens to a return statement inside a generator?","A `return x` sets `{ value: x, done: true }` on the final `next()` — but\n**`for...of` and spread ignore that value**; they only collect yielded\nvalues.\n\n```js\nfunction* g() { yield 1; return 99 }\n[...g()]                  \u002F\u002F [1]  — 99 dropped\nconst it = g()\nit.next()                 \u002F\u002F { value: 1, done: false }\nit.next()                 \u002F\u002F { value: 99, done: true }  visible here\n```\n\nTo observe the return value you must drive `next()` manually or capture it\nvia `yield*`.\n",{"id":86,"difficulty":70,"q":87,"a":88},"state-machine","How can a generator model a state machine?","Sequential `yield`s with a surrounding loop naturally encode states —\nexecution position *is* the current state, so you avoid explicit state\nvariables.\n\n```js\nfunction* traffic() {\n  while (true) {\n    yield 'green'; yield 'yellow'; yield 'red'  \u002F\u002F cycles states\n  }\n}\nconst light = traffic()\nlight.next().value  \u002F\u002F 'green'\nlight.next().value  \u002F\u002F 'yellow'\n```\n\nThe generator remembers where it paused, so each `next()` advances the\nmachine one transition with no bookkeeping.\n",{"id":90,"difficulty":14,"q":91,"a":92},"generator-vs-array","When is a generator better than building an array?","When the sequence is **large, infinite, or expensive**, and the consumer\nmay not need all of it. A generator streams values lazily; an array\nmaterializes everything up front.\n\n```js\n\u002F\u002F reads a huge file line by line without loading it all\nfunction* lines(text) {\n  for (const line of text.split('\\n')) yield line.trim()\n}\n```\n\nFor small, fully-consumed collections an array is simpler and faster to\nindex — generators shine on streaming\u002Fpipeline workloads.\n",{"id":94,"difficulty":14,"q":95,"a":96},"early-break-cleanup","What happens to a generator when you break out of for...of early?","`for...of` calls the iterator's **`return()`** on `break`, `throw`, or an\nexception — so a generator's `finally` runs and resources are released.\n\n```js\nfunction* withResource() {\n  try { while (true) yield acquire() }\n  finally { release() }   \u002F\u002F runs even on early break\n}\nfor (const x of withResource()) { if (done) break }  \u002F\u002F release() called\n```\n\nThis automatic cleanup is a key reason to wrap resource handling in\ntry\u002Ffinally inside generators.\n",{"id":98,"difficulty":14,"q":99,"a":100},"spread-consumes-iterator","Why does iterating the same generator twice yield nothing the second time?","A generator object is a **single-use** iterator — once `done`, it stays\ndone. It doesn't restart.\n\n```js\nconst g = (function* () { yield 1; yield 2 })()\n[...g]   \u002F\u002F [1, 2]\n[...g]   \u002F\u002F []  already exhausted\n```\n\nTo re-iterate, expose a **factory** (a function returning a fresh\ngenerator) or a class whose `[Symbol.iterator]` creates a new one each\ncall.\n",{"id":102,"difficulty":70,"q":103,"a":104},"async-generator","What is an async generator?","An **`async function*`** can `await` inside and `yield` values\nasynchronously. Its `next()` returns a **Promise** of `{ value, done }`,\nand you consume it with **`for await...of`**.\n\n```js\nasync function* pages(url) {\n  let next = url\n  while (next) {\n    const res = await fetch(next)   \u002F\u002F await inside\n    const data = await res.json()\n    yield data.items\n    next = data.nextPage\n  }\n}\nfor await (const items of pages('\u002Fapi')) { \u002F* ... *\u002F }\n```\n\nPerfect for paginated APIs and streams where each chunk arrives over time.\n",{"id":106,"difficulty":70,"q":107,"a":108},"for-await-of","What is for await...of and where can you use it?","It iterates an **async iterable** (one with `Symbol.asyncIterator`),\nawaiting each `next()` Promise before the loop body runs. It must be inside\nan `async` function.\n\n```js\nasync function read(stream) {\n  for await (const chunk of stream) {   \u002F\u002F awaits each chunk\n    process(chunk)\n  }\n}\n```\n\nIt also works on a plain array of Promises, awaiting each in turn — handy\nfor sequential async processing.\n",{"id":110,"difficulty":70,"q":111,"a":112},"symbol-asynciterator","How does Symbol.asyncIterator differ from Symbol.iterator?","`Symbol.iterator` returns an iterator whose `next()` yields plain\n`{ value, done }`; `Symbol.asyncIterator` returns one whose `next()` yields\na **Promise** of that. `for await...of` looks for `asyncIterator` first,\nfalling back to the sync one.\n\n```js\nconst stream = {\n  async *[Symbol.asyncIterator]() {\n    yield await getChunk()   \u002F\u002F async iterable\n  }\n}\n```\n\nUse the async variant when each value depends on I\u002FO or timing.\n",{"id":114,"difficulty":14,"q":115,"a":116},"generator-memory","How do generators help with memory usage?","They hold only the **current** value and the suspended call frame, not the\nwhole sequence — so you can process gigabytes by streaming a constant\namount of memory.\n\n```js\nfunction* range(n) { for (let i = 0; i \u003C n; i++) yield i }\nlet sum = 0\nfor (const i of range(1e9)) sum += i   \u002F\u002F no billion-element array\n```\n\nBuilding `Array.from({length: 1e9})` first would blow the heap; the\ngenerator never allocates it.\n",{"id":118,"difficulty":70,"q":119,"a":120},"generator-pipeline","How do you compose generators into a processing pipeline?","Chain generators that each take an iterable and yield a transformed one —\nlazy `map`\u002F`filter` that never build intermediate arrays.\n\n```js\nfunction* map(it, f) { for (const x of it) yield f(x) }\nfunction* filter(it, p) { for (const x of it) if (p(x)) yield x }\n\nconst result = map(\n  filter(range(100), n => n % 2 === 0),\n  n => n * n\n)   \u002F\u002F nothing computed until consumed\n```\n\nEach stage pulls one value at a time, so a pipeline over an infinite source\nstill works.\n",{"id":122,"difficulty":70,"q":123,"a":124},"generator-this-binding","Can a generator be an arrow function?","No — there is **no arrow generator syntax**. Generators must be\n`function*` declarations\u002Fexpressions or `*method()` shorthand in\nobjects\u002Fclasses.\n\n```js\nconst g = function* () { yield 1 }      \u002F\u002F expression\nconst obj = { *gen() { yield 1 } }      \u002F\u002F method shorthand\n\u002F\u002F const bad = *() => {}                \u002F\u002F SyntaxError\n```\n\nAs object\u002Fclass methods, generators get `this` from the call site like any\nmethod, which is exactly why arrow generators aren't needed.\n",{"id":126,"difficulty":14,"q":127,"a":128},"iterator-helpers","What are iterator helper methods?","Modern engines add lazy helper methods directly on iterators —\n`map`, `filter`, `take`, `drop`, `flatMap`, `reduce`, `toArray` — so you\nget array-like chaining without materializing arrays.\n\n```js\nfunction* naturals() { let n = 1; while (true) yield n++ }\nnaturals()\n  .filter(n => n % 2)\n  .map(n => n * n)\n  .take(3)\n  .toArray()           \u002F\u002F [1, 9, 25]  lazy, bounded by take\n```\n\nThey make working with infinite generators ergonomic; check target support\nas adoption is recent.\n",{"id":130,"difficulty":14,"q":131,"a":132},"manual-vs-generator-iterator","Why prefer a generator over hand-writing an iterator object?","A generator manages all the protocol state for you — the `{ value, done }`\nshape, the `done` terminator, returning itself from `Symbol.iterator`, and\n`return()`\u002F`throw()` semantics. Hand-rolled iterators are verbose and\nerror-prone.\n\n```js\n\u002F\u002F generator: 3 lines vs a dozen for the manual next()\u002Fdone version\nfunction* range(s, e) { for (let i = s; i \u003C e; i++) yield i }\n```\n\nReserve manual iterators for cases needing unusual control over the\nprotocol; otherwise use a generator.\n",{"id":134,"difficulty":14,"q":135,"a":136},"destructure-iterator","How do destructuring and spread interact with iterators?","Both consume an iterable via its iterator. Destructuring pulls only as many\nvalues as the pattern needs; spread drains it fully.\n\n```js\nfunction* g() { yield 1; yield 2; yield 3 }\nconst [a, b] = g()     \u002F\u002F pulls 2, iterator left at 3\nconst all = [...g()]   \u002F\u002F [1, 2, 3] — fully drained\n```\n\nBecause destructuring stops early, `const [first] = infinite()` is safe,\nbut `const [...rest] = infinite()` hangs.\n",{"id":138,"difficulty":70,"q":139,"a":140},"generator-return-from-yieldstar","How do you capture the return value of a delegated generator?","`yield*` evaluates to the **return value** of the inner generator, letting\nyou build sub-results.\n\n```js\nfunction* inner() { yield 1; return 'done' }\nfunction* outer() {\n  const result = yield* inner()   \u002F\u002F result === 'done'\n  yield result\n}\n[...outer()]   \u002F\u002F [1, 'done']\n```\n\nThis is how generators compose computations: the yielded stream flows\nthrough while the return value bubbles back to the delegator.\n",{"id":142,"difficulty":14,"q":143,"a":144},"when-not-to-use-generators","When should you avoid generators?","For small, fully-consumed collections, plain arrays\u002Farray methods are\nsimpler, faster to index, and easier to debug. Generators add overhead per\n`next()` call and can't be randomly accessed or reused.\n\n```js\n\u002F\u002F overkill — just use an array\nfunction* three() { yield 1; yield 2; yield 3 }\nconst arr = [1, 2, 3]   \u002F\u002F clearer here\n```\n\nReach for generators when you genuinely need laziness, infinite\u002Fstreaming\ndata, two-way communication, or memory-bounded processing.\n",null,{"description":11},"JavaScript generators and iterators interview questions — the iterator and iterable protocols, function* and yield, yield* delegation, lazy and infinite sequences, two-way communication and async generators.","javascript\u002Ffunctions\u002Fgenerators-iterators","Generators & Iterators","Functions","functions","2026-06-18","PjkAQb6KJH099D5oXrnCdLp3so5iSdXLlO3hoWit9iw",[155,159,162,166,170],{"subtopic":156,"path":157,"order":158},"Closures","\u002Fjavascript\u002Ffunctions\u002Fclosures",1,{"subtopic":160,"path":161,"order":12},"The this Keyword","\u002Fjavascript\u002Ffunctions\u002Fthis-keyword",{"subtopic":163,"path":164,"order":165},"Higher-Order Functions","\u002Fjavascript\u002Ffunctions\u002Fhigher-order-functions",3,{"subtopic":167,"path":168,"order":169},"Function Types & Parameters","\u002Fjavascript\u002Ffunctions\u002Ffunction-types-parameters",4,{"subtopic":149,"path":21,"order":20},{"path":172,"title":173},"\u002Fblog\u002Fjavascript-generators-iterators-explained","JavaScript Generators & Iterators Explained — Lazy Sequences and the Iteration Protocols",1781808676370]