[{"data":1,"prerenderedAt":1528},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjavascript-generators-iterators-explained":3},{"id":4,"title":5,"body":6,"description":1513,"difficulty":1514,"extension":1515,"framework":1516,"frameworkSlug":1517,"meta":1518,"navigation":737,"order":136,"path":1519,"qaPath":1520,"seo":1521,"stem":1522,"subtopic":1523,"topic":1524,"topicSlug":1525,"updated":1526,"__hash__":1527},"blog\u002Fblog\u002Fjavascript-generators-iterators-explained.md","JavaScript Generators & Iterators Explained — Lazy Sequences and the Iteration Protocols",{"type":7,"value":8,"toc":1498},"minimark",[9,14,40,44,65,223,226,230,253,430,448,452,474,553,559,563,566,655,672,676,683,774,795,799,807,890,899,903,917,1011,1024,1028,1044,1141,1144,1148,1155,1211,1221,1225,1246,1391,1394,1398,1419,1423,1491,1494],[10,11,13],"h2",{"id":12},"lazy-pausable-functions","Lazy, pausable functions",[15,16,17,18,22,23,26,27,30,31,34,35,39],"p",{},"Most functions run start to finish in one shot. ",[19,20,21],"strong",{},"Generators"," are different: they can\n",[19,24,25],{},"pause"," mid-execution and ",[19,28,29],{},"resume"," later, producing a sequence of values on demand. This\nmakes them ideal for lazy evaluation, infinite sequences, and streaming data without loading\neverything into memory. Generators are built on JavaScript's ",[19,32,33],{},"iteration protocols",", the same\nmachinery that powers ",[36,37,38],"code",{},"for...of",", spread, and destructuring. Understanding both gives you a\npowerful, often-underused tool — and explains how iteration works throughout the language.",[10,41,43],{"id":42},"the-iterator-protocol","The iterator protocol",[15,45,46,47,50,51,56,57,60,61,64],{},"An ",[19,48,49],{},"iterator"," is any object with a ",[19,52,53],{},[36,54,55],{},"next()"," method that returns ",[36,58,59],{},"{ value, done }",". Each\ncall advances the sequence; ",[36,62,63],{},"done: true"," signals the end.",[66,67,72],"pre",{"className":68,"code":69,"language":70,"meta":71,"style":71},"language-js shiki shiki-themes github-light github-dark","const 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 }\nit.next()   \u002F\u002F { value: 1, done: false }\n","js","",[36,73,74,94,106,116,134,161,184,190,196,211],{"__ignoreMap":71},[75,76,79,83,87,90],"span",{"class":77,"line":78},"line",1,[75,80,82],{"class":81},"szBVR","const",[75,84,86],{"class":85},"sj4cs"," it",[75,88,89],{"class":81}," =",[75,91,93],{"class":92},"sVt8B"," {\n",[75,95,97,100,103],{"class":77,"line":96},2,[75,98,99],{"class":92},"  i: ",[75,101,102],{"class":85},"0",[75,104,105],{"class":92},",\n",[75,107,109,113],{"class":77,"line":108},3,[75,110,112],{"class":111},"sScJk","  next",[75,114,115],{"class":92},"() {\n",[75,117,119,122,125,128,131],{"class":77,"line":118},4,[75,120,121],{"class":81},"    return",[75,123,124],{"class":85}," this",[75,126,127],{"class":92},".i ",[75,129,130],{"class":81},"\u003C",[75,132,133],{"class":85}," 3\n",[75,135,137,140,143,146,149,152,155,158],{"class":77,"line":136},5,[75,138,139],{"class":81},"      ?",[75,141,142],{"class":92}," { value: ",[75,144,145],{"class":85},"this",[75,147,148],{"class":92},".i",[75,150,151],{"class":81},"++",[75,153,154],{"class":92},", done: ",[75,156,157],{"class":85},"false",[75,159,160],{"class":92}," }\n",[75,162,164,167,169,172,174,177,180],{"class":77,"line":163},6,[75,165,166],{"class":81},"      :",[75,168,142],{"class":92},[75,170,171],{"class":85},"undefined",[75,173,154],{"class":92},[75,175,176],{"class":85},"true",[75,178,179],{"class":92}," }   ",[75,181,183],{"class":182},"sJ8bj","\u002F\u002F terminator\n",[75,185,187],{"class":77,"line":186},7,[75,188,189],{"class":92},"  }\n",[75,191,193],{"class":77,"line":192},8,[75,194,195],{"class":92},"}\n",[75,197,199,202,205,208],{"class":77,"line":198},9,[75,200,201],{"class":92},"it.",[75,203,204],{"class":111},"next",[75,206,207],{"class":92},"()   ",[75,209,210],{"class":182},"\u002F\u002F { value: 0, done: false }\n",[75,212,214,216,218,220],{"class":77,"line":213},10,[75,215,201],{"class":92},[75,217,204],{"class":111},[75,219,207],{"class":92},[75,221,222],{"class":182},"\u002F\u002F { value: 1, done: false }\n",[15,224,225],{},"This simple contract is what every iteration construct ultimately calls.",[10,227,229],{"id":228},"the-iterable-protocol","The iterable protocol",[15,231,46,232,235,236,241,242,244,245,248,249,252],{},[19,233,234],{},"iterable"," is an object with a ",[19,237,238],{},[36,239,240],{},"[Symbol.iterator]()"," method that returns an iterator.\nThe iterable is the collection; the iterator is the cursor. ",[36,243,38],{},", spread, ",[36,246,247],{},"Array.from",",\nand destructuring all look for ",[36,250,251],{},"Symbol.iterator",".",[66,254,256],{"className":68,"code":255,"language":70,"meta":71,"style":71},"const range = {\n  from: 1, to: 3,\n  [Symbol.iterator]() {\n    let current = this.from\n    const last = this.to\n    return {\n      next: () => current \u003C= last\n        ? { value: current++, done: false }\n        : { value: undefined, done: true }\n    }\n  }\n}\n[...range]                  \u002F\u002F [1, 2, 3]\nfor (const n of range) {}   \u002F\u002F works\n",[36,257,258,269,285,290,306,321,327,346,362,377,382,387,392,407],{"__ignoreMap":71},[75,259,260,262,265,267],{"class":77,"line":78},[75,261,82],{"class":81},[75,263,264],{"class":85}," range",[75,266,89],{"class":81},[75,268,93],{"class":92},[75,270,271,274,277,280,283],{"class":77,"line":96},[75,272,273],{"class":92},"  from: ",[75,275,276],{"class":85},"1",[75,278,279],{"class":92},", to: ",[75,281,282],{"class":85},"3",[75,284,105],{"class":92},[75,286,287],{"class":77,"line":108},[75,288,289],{"class":92},"  [Symbol.iterator]() {\n",[75,291,292,295,298,301,303],{"class":77,"line":118},[75,293,294],{"class":81},"    let",[75,296,297],{"class":92}," current ",[75,299,300],{"class":81},"=",[75,302,124],{"class":85},[75,304,305],{"class":92},".from\n",[75,307,308,311,314,316,318],{"class":77,"line":136},[75,309,310],{"class":81},"    const",[75,312,313],{"class":85}," last",[75,315,89],{"class":81},[75,317,124],{"class":85},[75,319,320],{"class":92},".to\n",[75,322,323,325],{"class":77,"line":163},[75,324,121],{"class":81},[75,326,93],{"class":92},[75,328,329,332,335,338,340,343],{"class":77,"line":186},[75,330,331],{"class":111},"      next",[75,333,334],{"class":92},": () ",[75,336,337],{"class":81},"=>",[75,339,297],{"class":92},[75,341,342],{"class":81},"\u003C=",[75,344,345],{"class":92}," last\n",[75,347,348,351,354,356,358,360],{"class":77,"line":192},[75,349,350],{"class":81},"        ?",[75,352,353],{"class":92}," { value: current",[75,355,151],{"class":81},[75,357,154],{"class":92},[75,359,157],{"class":85},[75,361,160],{"class":92},[75,363,364,367,369,371,373,375],{"class":77,"line":198},[75,365,366],{"class":81},"        :",[75,368,142],{"class":92},[75,370,171],{"class":85},[75,372,154],{"class":92},[75,374,176],{"class":85},[75,376,160],{"class":92},[75,378,379],{"class":77,"line":213},[75,380,381],{"class":92},"    }\n",[75,383,385],{"class":77,"line":384},11,[75,386,189],{"class":92},[75,388,390],{"class":77,"line":389},12,[75,391,195],{"class":92},[75,393,395,398,401,404],{"class":77,"line":394},13,[75,396,397],{"class":92},"[",[75,399,400],{"class":81},"...",[75,402,403],{"class":92},"range]                  ",[75,405,406],{"class":182},"\u002F\u002F [1, 2, 3]\n",[75,408,410,413,416,418,421,424,427],{"class":77,"line":409},14,[75,411,412],{"class":81},"for",[75,414,415],{"class":92}," (",[75,417,82],{"class":81},[75,419,420],{"class":85}," n",[75,422,423],{"class":81}," of",[75,425,426],{"class":92}," range) {}   ",[75,428,429],{"class":182},"\u002F\u002F works\n",[15,431,432,433,436,437,440,441,443,444,447],{},"Built-ins like arrays, strings, ",[36,434,435],{},"Map",", and ",[36,438,439],{},"Set"," all implement this protocol — which is why\nthey work with ",[36,442,38],{},". Plain objects don't, which is why ",[36,445,446],{},"for...of {}"," throws.",[10,449,451],{"id":450},"generators-protocols-made-easy","Generators: protocols made easy",[15,453,454,455,415,458,461,462,466,467,469,470,473],{},"Writing iterators by hand is tedious. A ",[19,456,457],{},"generator function",[36,459,460],{},"function*",") produces an object\nthat is ",[463,464,465],"em",{},"both"," an iterator and an iterable, managing all the ",[36,468,59],{}," bookkeeping for\nyou. Each ",[36,471,472],{},"yield"," pauses and emits a value.",[66,475,477],{"className":68,"code":476,"language":70,"meta":71,"style":71},"function* gen() {\n  yield 1\n  yield 2\n  yield 3\n}\nconst g = gen()\ng.next()   \u002F\u002F { value: 1, done: false }\n[...gen()] \u002F\u002F [1, 2, 3] spreadable directly\n",[36,478,479,488,496,503,509,513,527,538],{"__ignoreMap":71},[75,480,481,483,486],{"class":77,"line":78},[75,482,460],{"class":81},[75,484,485],{"class":111}," gen",[75,487,115],{"class":92},[75,489,490,493],{"class":77,"line":96},[75,491,492],{"class":81},"  yield",[75,494,495],{"class":85}," 1\n",[75,497,498,500],{"class":77,"line":108},[75,499,492],{"class":81},[75,501,502],{"class":85}," 2\n",[75,504,505,507],{"class":77,"line":118},[75,506,492],{"class":81},[75,508,133],{"class":85},[75,510,511],{"class":77,"line":136},[75,512,195],{"class":92},[75,514,515,517,520,522,524],{"class":77,"line":163},[75,516,82],{"class":81},[75,518,519],{"class":85}," g",[75,521,89],{"class":81},[75,523,485],{"class":111},[75,525,526],{"class":92},"()\n",[75,528,529,532,534,536],{"class":77,"line":186},[75,530,531],{"class":92},"g.",[75,533,204],{"class":111},[75,535,207],{"class":92},[75,537,222],{"class":182},[75,539,540,542,544,547,550],{"class":77,"line":192},[75,541,397],{"class":92},[75,543,400],{"class":81},[75,545,546],{"class":111},"gen",[75,548,549],{"class":92},"()] ",[75,551,552],{"class":182},"\u002F\u002F [1, 2, 3] spreadable directly\n",[15,554,555,556,558],{},"Calling a generator doesn't run its body — it returns a generator object. The body advances\nonly when you call ",[36,557,55],{}," (or iterate). This pause\u002Fresume is the entire point.",[10,560,562],{"id":561},"rewriting-range-as-a-generator","Rewriting range as a generator",[15,564,565],{},"Compare the verbose manual iterator above with the generator version — the difference is\ndramatic.",[66,567,569],{"className":68,"code":568,"language":70,"meta":71,"style":71},"function* range(from, to) {\n  for (let i = from; i \u003C= to; i++) yield i   \u002F\u002F all bookkeeping handled\n}\n[...range(1, 3)]   \u002F\u002F [1, 2, 3]\n",[36,570,571,593,629,633],{"__ignoreMap":71},[75,572,573,575,577,580,584,587,590],{"class":77,"line":78},[75,574,460],{"class":81},[75,576,264],{"class":111},[75,578,579],{"class":92},"(",[75,581,583],{"class":582},"s4XuR","from",[75,585,586],{"class":92},", ",[75,588,589],{"class":582},"to",[75,591,592],{"class":92},") {\n",[75,594,595,598,600,603,606,608,611,613,616,618,621,623,626],{"class":77,"line":96},[75,596,597],{"class":81},"  for",[75,599,415],{"class":92},[75,601,602],{"class":81},"let",[75,604,605],{"class":92}," i ",[75,607,300],{"class":81},[75,609,610],{"class":92}," from; i ",[75,612,342],{"class":81},[75,614,615],{"class":92}," to; i",[75,617,151],{"class":81},[75,619,620],{"class":92},") ",[75,622,472],{"class":81},[75,624,625],{"class":92}," i   ",[75,627,628],{"class":182},"\u002F\u002F all bookkeeping handled\n",[75,630,631],{"class":77,"line":108},[75,632,195],{"class":92},[75,634,635,637,639,642,644,646,648,650,653],{"class":77,"line":118},[75,636,397],{"class":92},[75,638,400],{"class":81},[75,640,641],{"class":111},"range",[75,643,579],{"class":92},[75,645,276],{"class":85},[75,647,586],{"class":92},[75,649,282],{"class":85},[75,651,652],{"class":92},")]   ",[75,654,406],{"class":182},[15,656,657,658,661,662,664,665,667,668,671],{},"To make a class iterable, define ",[36,659,660],{},"[Symbol.iterator]"," as a generator method and ",[36,663,472],{}," the\nelements — no ",[36,666,55],{},"\u002F",[36,669,670],{},"done"," plumbing required.",[10,673,675],{"id":674},"lazy-and-infinite-sequences","Lazy and infinite sequences",[15,677,678,679,682],{},"Because generators compute values only when asked, they can represent ",[19,680,681],{},"infinite"," sequences\nsafely, as long as the consumer stops pulling.",[66,684,686],{"className":68,"code":685,"language":70,"meta":71,"style":71},"function* naturals() {\n  let n = 1\n  while (true) yield n++       \u002F\u002F infinite, but lazy\n}\n\nconst it = naturals()\nit.next().value   \u002F\u002F 1 — only this much computed\nit.next().value   \u002F\u002F 2\n",[36,687,688,697,709,729,733,739,751,763],{"__ignoreMap":71},[75,689,690,692,695],{"class":77,"line":78},[75,691,460],{"class":81},[75,693,694],{"class":111}," naturals",[75,696,115],{"class":92},[75,698,699,702,705,707],{"class":77,"line":96},[75,700,701],{"class":81},"  let",[75,703,704],{"class":92}," n ",[75,706,300],{"class":81},[75,708,495],{"class":85},[75,710,711,714,716,718,720,722,724,726],{"class":77,"line":108},[75,712,713],{"class":81},"  while",[75,715,415],{"class":92},[75,717,176],{"class":85},[75,719,620],{"class":92},[75,721,472],{"class":81},[75,723,420],{"class":92},[75,725,151],{"class":81},[75,727,728],{"class":182},"       \u002F\u002F infinite, but lazy\n",[75,730,731],{"class":77,"line":118},[75,732,195],{"class":92},[75,734,735],{"class":77,"line":136},[75,736,738],{"emptyLinePlaceholder":737},true,"\n",[75,740,741,743,745,747,749],{"class":77,"line":163},[75,742,82],{"class":81},[75,744,86],{"class":85},[75,746,89],{"class":81},[75,748,694],{"class":111},[75,750,526],{"class":92},[75,752,753,755,757,760],{"class":77,"line":186},[75,754,201],{"class":92},[75,756,204],{"class":111},[75,758,759],{"class":92},"().value   ",[75,761,762],{"class":182},"\u002F\u002F 1 — only this much computed\n",[75,764,765,767,769,771],{"class":77,"line":192},[75,766,201],{"class":92},[75,768,204],{"class":111},[75,770,759],{"class":92},[75,772,773],{"class":182},"\u002F\u002F 2\n",[15,775,776,777,780,781,783,784,787,788,791,792,794],{},"The pitfall: ",[36,778,779],{},"[...naturals()]"," or a ",[36,782,38],{}," without a ",[36,785,786],{},"break"," will loop forever. Always\nbound consumption (a ",[36,789,790],{},"take(n)"," helper, a ",[36,793,786],{},", or destructuring a fixed count).",[10,796,798],{"id":797},"yield-delegation","yield* — delegation",[15,800,801,806],{},[19,802,803],{},[36,804,805],{},"yield*"," delegates to another iterable, yielding all of its values inline. It's perfect for\ncomposing generators or flattening structures.",[66,808,810],{"className":68,"code":809,"language":70,"meta":71,"style":71},"function* 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",[36,811,812,837,846,853,865,871,875],{"__ignoreMap":71},[75,813,814,816,819,822,824,827,830,832,835],{"class":77,"line":78},[75,815,460],{"class":81},[75,817,818],{"class":111}," inner",[75,820,821],{"class":92},"() { ",[75,823,472],{"class":81},[75,825,826],{"class":85}," 1",[75,828,829],{"class":92},"; ",[75,831,472],{"class":81},[75,833,834],{"class":85}," 2",[75,836,160],{"class":92},[75,838,839,841,844],{"class":77,"line":96},[75,840,460],{"class":81},[75,842,843],{"class":111}," outer",[75,845,115],{"class":92},[75,847,848,850],{"class":77,"line":108},[75,849,492],{"class":81},[75,851,852],{"class":85}," 0\n",[75,854,855,858,860,862],{"class":77,"line":118},[75,856,857],{"class":81},"  yield*",[75,859,818],{"class":111},[75,861,207],{"class":92},[75,863,864],{"class":182},"\u002F\u002F yields 1, then 2\n",[75,866,867,869],{"class":77,"line":136},[75,868,492],{"class":81},[75,870,133],{"class":85},[75,872,873],{"class":77,"line":163},[75,874,195],{"class":92},[75,876,877,879,881,884,887],{"class":77,"line":186},[75,878,397],{"class":92},[75,880,400],{"class":81},[75,882,883],{"class":111},"outer",[75,885,886],{"class":92},"()]   ",[75,888,889],{"class":182},"\u002F\u002F [0, 1, 2, 3]\n",[15,891,892,894,895,898],{},[36,893,805],{}," works on any iterable (arrays, strings, Sets), making recursive flattening elegant:\n",[36,896,897],{},"yield* flatten(child)"," for nested data.",[10,900,902],{"id":901},"two-way-communication","Two-way communication",[15,904,905,906,909,910,913,914,916],{},"Generators aren't just producers — they can ",[19,907,908],{},"receive"," values. The argument to ",[36,911,912],{},"next(value)","\nbecomes the result of the ",[36,915,472],{}," expression the generator is paused on.",[66,918,920],{"className":68,"code":919,"language":70,"meta":71,"style":71},"function* conversation() {\n  const name = yield 'What is your name?'   \u002F\u002F receives the next() argument\n  yield `Hello, ${name}!`\n}\nconst c = conversation()\nc.next().value        \u002F\u002F 'What is your name?'\nc.next('Ada').value   \u002F\u002F 'Hello, Ada!' 'Ada' became `name`\n",[36,921,922,931,951,964,968,981,994],{"__ignoreMap":71},[75,923,924,926,929],{"class":77,"line":78},[75,925,460],{"class":81},[75,927,928],{"class":111}," conversation",[75,930,115],{"class":92},[75,932,933,936,939,941,944,948],{"class":77,"line":96},[75,934,935],{"class":81},"  const",[75,937,938],{"class":85}," name",[75,940,89],{"class":81},[75,942,943],{"class":81}," yield",[75,945,947],{"class":946},"sZZnC"," 'What is your name?'",[75,949,950],{"class":182},"   \u002F\u002F receives the next() argument\n",[75,952,953,955,958,961],{"class":77,"line":108},[75,954,492],{"class":81},[75,956,957],{"class":946}," `Hello, ${",[75,959,960],{"class":92},"name",[75,962,963],{"class":946},"}!`\n",[75,965,966],{"class":77,"line":118},[75,967,195],{"class":92},[75,969,970,972,975,977,979],{"class":77,"line":136},[75,971,82],{"class":81},[75,973,974],{"class":85}," c",[75,976,89],{"class":81},[75,978,928],{"class":111},[75,980,526],{"class":92},[75,982,983,986,988,991],{"class":77,"line":163},[75,984,985],{"class":92},"c.",[75,987,204],{"class":111},[75,989,990],{"class":92},"().value        ",[75,992,993],{"class":182},"\u002F\u002F 'What is your name?'\n",[75,995,996,998,1000,1002,1005,1008],{"class":77,"line":186},[75,997,985],{"class":92},[75,999,204],{"class":111},[75,1001,579],{"class":92},[75,1003,1004],{"class":946},"'Ada'",[75,1006,1007],{"class":92},").value   ",[75,1009,1010],{"class":182},"\u002F\u002F 'Hello, Ada!' 'Ada' became `name`\n",[15,1012,1013,1014,1017,1018,1020,1021,1023],{},"Note the ",[19,1015,1016],{},"first"," ",[36,1019,55],{}," argument is ignored — there's no paused ",[36,1022,472],{}," yet to receive it.\nThis two-way channel enables coroutine-style control flow.",[10,1025,1027],{"id":1026},"early-termination-and-cleanup","Early termination and cleanup",[15,1029,1030,1032,1033,1036,1037,1039,1040,1043],{},[36,1031,38],{}," automatically calls the generator's ",[36,1034,1035],{},"return()"," when you ",[36,1038,786],{}," or throw, running\nany ",[36,1041,1042],{},"finally"," block — so resources get released.",[66,1045,1047],{"className":68,"code":1046,"language":70,"meta":71,"style":71},"function* withResource() {\n  try {\n    while (true) yield acquire()\n  } finally {\n    release()   \u002F\u002F runs even on early break\n  }\n}\nfor (const x of withResource()) { if (done) break }   \u002F\u002F release() called\n",[36,1048,1049,1058,1065,1083,1092,1102,1106,1110],{"__ignoreMap":71},[75,1050,1051,1053,1056],{"class":77,"line":78},[75,1052,460],{"class":81},[75,1054,1055],{"class":111}," withResource",[75,1057,115],{"class":92},[75,1059,1060,1063],{"class":77,"line":96},[75,1061,1062],{"class":81},"  try",[75,1064,93],{"class":92},[75,1066,1067,1070,1072,1074,1076,1078,1081],{"class":77,"line":108},[75,1068,1069],{"class":81},"    while",[75,1071,415],{"class":92},[75,1073,176],{"class":85},[75,1075,620],{"class":92},[75,1077,472],{"class":81},[75,1079,1080],{"class":111}," acquire",[75,1082,526],{"class":92},[75,1084,1085,1088,1090],{"class":77,"line":118},[75,1086,1087],{"class":92},"  } ",[75,1089,1042],{"class":81},[75,1091,93],{"class":92},[75,1093,1094,1097,1099],{"class":77,"line":136},[75,1095,1096],{"class":111},"    release",[75,1098,207],{"class":92},[75,1100,1101],{"class":182},"\u002F\u002F runs even on early break\n",[75,1103,1104],{"class":77,"line":163},[75,1105,189],{"class":92},[75,1107,1108],{"class":77,"line":186},[75,1109,195],{"class":92},[75,1111,1112,1114,1116,1118,1121,1123,1125,1128,1131,1134,1136,1138],{"class":77,"line":192},[75,1113,412],{"class":81},[75,1115,415],{"class":92},[75,1117,82],{"class":81},[75,1119,1120],{"class":85}," x",[75,1122,423],{"class":81},[75,1124,1055],{"class":111},[75,1126,1127],{"class":92},"()) { ",[75,1129,1130],{"class":81},"if",[75,1132,1133],{"class":92}," (done) ",[75,1135,786],{"class":81},[75,1137,179],{"class":92},[75,1139,1140],{"class":182},"\u002F\u002F release() called\n",[15,1142,1143],{},"This makes generators a clean way to manage resources tied to iteration.",[10,1145,1147],{"id":1146},"single-use-iterators","Single-use iterators",[15,1149,1150,1151,1154],{},"A generator object is a ",[19,1152,1153],{},"one-shot"," iterator — once exhausted, it stays done and won't\nrestart.",[66,1156,1158],{"className":68,"code":1157,"language":70,"meta":71,"style":71},"const g = (function* () { yield 1; yield 2 })()\n[...g]   \u002F\u002F [1, 2]\n[...g]   \u002F\u002F [] already consumed\n",[36,1159,1160,1188,1200],{"__ignoreMap":71},[75,1161,1162,1164,1166,1168,1170,1172,1175,1177,1179,1181,1183,1185],{"class":77,"line":78},[75,1163,82],{"class":81},[75,1165,519],{"class":85},[75,1167,89],{"class":81},[75,1169,415],{"class":92},[75,1171,460],{"class":81},[75,1173,1174],{"class":92}," () { ",[75,1176,472],{"class":81},[75,1178,826],{"class":85},[75,1180,829],{"class":92},[75,1182,472],{"class":81},[75,1184,834],{"class":85},[75,1186,1187],{"class":92}," })()\n",[75,1189,1190,1192,1194,1197],{"class":77,"line":96},[75,1191,397],{"class":92},[75,1193,400],{"class":81},[75,1195,1196],{"class":92},"g]   ",[75,1198,1199],{"class":182},"\u002F\u002F [1, 2]\n",[75,1201,1202,1204,1206,1208],{"class":77,"line":108},[75,1203,397],{"class":92},[75,1205,400],{"class":81},[75,1207,1196],{"class":92},[75,1209,1210],{"class":182},"\u002F\u002F [] already consumed\n",[15,1212,1213,1214,1217,1218,1220],{},"To re-iterate, expose a ",[19,1215,1216],{},"factory"," (a function returning a fresh generator) or a class whose\n",[36,1219,660],{}," creates a new one each call.",[10,1222,1224],{"id":1223},"async-generators","Async generators",[15,1226,46,1227,1232,1233,1236,1237,1239,1240,1245],{},[19,1228,1229],{},[36,1230,1231],{},"async function*"," can ",[36,1234,1235],{},"await"," inside and ",[36,1238,472],{}," asynchronously; you consume it with\n",[19,1241,1242],{},[36,1243,1244],{},"for await...of",". This is ideal for paginated APIs and streams where each chunk arrives over\ntime.",[66,1247,1249],{"className":68,"code":1248,"language":70,"meta":71,"style":71},"async 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\u002Fitems')) {\n  process(items)\n}\n",[36,1250,1251,1269,1281,1288,1309,1328,1336,1346,1350,1354,1379,1387],{"__ignoreMap":71},[75,1252,1253,1256,1259,1262,1264,1267],{"class":77,"line":78},[75,1254,1255],{"class":81},"async",[75,1257,1258],{"class":81}," function*",[75,1260,1261],{"class":111}," pages",[75,1263,579],{"class":92},[75,1265,1266],{"class":582},"url",[75,1268,592],{"class":92},[75,1270,1271,1273,1276,1278],{"class":77,"line":96},[75,1272,701],{"class":81},[75,1274,1275],{"class":92}," next ",[75,1277,300],{"class":81},[75,1279,1280],{"class":92}," url\n",[75,1282,1283,1285],{"class":77,"line":108},[75,1284,713],{"class":81},[75,1286,1287],{"class":92}," (next) {\n",[75,1289,1290,1292,1295,1297,1300,1303,1306],{"class":77,"line":118},[75,1291,310],{"class":81},[75,1293,1294],{"class":85}," res",[75,1296,89],{"class":81},[75,1298,1299],{"class":81}," await",[75,1301,1302],{"class":111}," fetch",[75,1304,1305],{"class":92},"(next)      ",[75,1307,1308],{"class":182},"\u002F\u002F await inside\n",[75,1310,1311,1313,1316,1318,1320,1323,1326],{"class":77,"line":136},[75,1312,310],{"class":81},[75,1314,1315],{"class":85}," data",[75,1317,89],{"class":81},[75,1319,1299],{"class":81},[75,1321,1322],{"class":92}," res.",[75,1324,1325],{"class":111},"json",[75,1327,526],{"class":92},[75,1329,1330,1333],{"class":77,"line":163},[75,1331,1332],{"class":81},"    yield",[75,1334,1335],{"class":92}," data.items\n",[75,1337,1338,1341,1343],{"class":77,"line":186},[75,1339,1340],{"class":92},"    next ",[75,1342,300],{"class":81},[75,1344,1345],{"class":92}," data.nextPage\n",[75,1347,1348],{"class":77,"line":192},[75,1349,189],{"class":92},[75,1351,1352],{"class":77,"line":198},[75,1353,195],{"class":92},[75,1355,1356,1358,1360,1362,1364,1367,1369,1371,1373,1376],{"class":77,"line":213},[75,1357,412],{"class":81},[75,1359,1299],{"class":81},[75,1361,415],{"class":92},[75,1363,82],{"class":81},[75,1365,1366],{"class":85}," items",[75,1368,423],{"class":81},[75,1370,1261],{"class":111},[75,1372,579],{"class":92},[75,1374,1375],{"class":946},"'\u002Fapi\u002Fitems'",[75,1377,1378],{"class":92},")) {\n",[75,1380,1381,1384],{"class":77,"line":384},[75,1382,1383],{"class":111},"  process",[75,1385,1386],{"class":92},"(items)\n",[75,1388,1389],{"class":77,"line":389},[75,1390,195],{"class":92},[15,1392,1393],{},"Async generators combine lazy iteration with asynchrony — streaming data on demand, one awaited\nchunk at a time.",[10,1395,1397],{"id":1396},"when-to-use-generators","When to use generators",[15,1399,1400,1401,1404,1405,586,1408,1411,1412,1415,1416,1418],{},"Reach for generators when you need ",[19,1402,1403],{},"laziness"," (compute on demand), ",[19,1406,1407],{},"infinite or streaming\nsequences",[19,1409,1410],{},"memory-bounded processing"," of huge data, or ",[19,1413,1414],{},"two-way coroutine"," control.\nFor small, fully-consumed collections, plain arrays and array methods are simpler and faster to\nindex. Generators add per-",[36,1417,55],{}," overhead and can't be randomly accessed or reused — so use\nthem where their unique powers actually pay off.",[10,1420,1422],{"id":1421},"key-takeaways","Key takeaways",[1424,1425,1426,1447,1457,1467,1475,1483],"ul",{},[1427,1428,46,1429,1431,1432,1434,1435,1437,1438,1440,1441,1443,1444,1446],"li",{},[19,1430,49],{}," has ",[36,1433,55],{}," returning ",[36,1436,59],{},"; an ",[19,1439,234],{}," has\n",[36,1442,240],{}," returning an iterator — the basis of ",[36,1445,38],{},", spread, destructuring.",[1427,1448,1449,415,1451,1453,1454,1456],{},[19,1450,21],{},[36,1452,460],{}," with ",[36,1455,472],{},") produce objects that are both, handling all\nprotocol bookkeeping and pausing\u002Fresuming on demand.",[1427,1458,1459,1460,1463,1464,1466],{},"They enable ",[19,1461,1462],{},"lazy"," and ",[19,1465,681],{}," sequences — but bound consumption or you'll loop\nforever.",[1427,1468,1469,1471,1472,1474],{},[36,1470,805],{}," delegates to other iterables; ",[36,1473,912],{}," enables two-way communication.",[1427,1476,1477,1478,1480,1481,252],{},"Generators are single-use; expose a factory to re-iterate. ",[36,1479,38],{}," triggers cleanup via\n",[36,1482,1035],{},[1427,1484,1485,1487,1488,1490],{},[19,1486,1224],{}," + ",[36,1489,1244],{}," stream asynchronous data like paginated APIs.",[15,1492,1493],{},"Generators turn iteration into something you control step by step — lazy, composable, and\nmemory-friendly — and reveal the protocols that make all JavaScript iteration tick.",[1495,1496,1497],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":71,"searchDepth":96,"depth":96,"links":1499},[1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512],{"id":12,"depth":96,"text":13},{"id":42,"depth":96,"text":43},{"id":228,"depth":96,"text":229},{"id":450,"depth":96,"text":451},{"id":561,"depth":96,"text":562},{"id":674,"depth":96,"text":675},{"id":797,"depth":96,"text":798},{"id":901,"depth":96,"text":902},{"id":1026,"depth":96,"text":1027},{"id":1146,"depth":96,"text":1147},{"id":1223,"depth":96,"text":1224},{"id":1396,"depth":96,"text":1397},{"id":1421,"depth":96,"text":1422},"Understand JavaScript generators and iterators — the iterator and iterable protocols, function* and yield, lazy and infinite sequences, two-way communication, and async generators.","medium","md","JavaScript","javascript",{},"\u002Fblog\u002Fjavascript-generators-iterators-explained","\u002Fjavascript\u002Ffunctions\u002Fgenerators-iterators",{"title":5,"description":1513},"blog\u002Fjavascript-generators-iterators-explained","Generators & Iterators","Functions","functions","2026-06-18","9hGj5D4N65Y4Fp6ggC16t4hMufwzKEkO37rPoNfDD0E",1781808673080]