[{"data":1,"prerenderedAt":3594},["ShallowReactive",2],{"page-\u002Fdart\u002F09-async-programming":3},{"id":4,"title":5,"body":6,"description":3587,"extension":3588,"meta":3589,"navigation":63,"path":3590,"seo":3591,"stem":3592,"__hash__":3593},"content\u002Fdart\u002F09-async-programming.md","Dart — Async Internals, Futures & Stream Pipelines",{"type":7,"value":8,"toc":3573},"minimark",[9,13,18,262,266,779,784,1111,1115,1606,1610,2032,2039,2488,2492,2776,2780,2858,2862,3037,3041,3048,3196,3199,3569],[10,11,5],"h1",{"id":12},"dart-async-internals-futures-stream-pipelines",[14,15,17],"h2",{"id":16},"event-loop-microtask-vs-event-queues","Event Loop — Microtask vs Event Queues",[19,20,22],"code-wrapper",{"language":21},"dart",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-dart shiki shiki-themes github-light github-dark","\u002F\u002F Dart's event loop has TWO queues:\n\u002F\u002F 1. Microtask queue — runs BEFORE event queue. For internal Dart bookkeeping.\n\u002F\u002F    Use Future.microtask() for \"run ASAP, but after current sync code.\"\n\u002F\u002F 2. Event queue — I\u002FO, timers, user events. Use Future() \u002F Future.delayed().\n\nvoid main() async {\n  print('1 — sync');\n\n  Future(() => print('4 — event queue (Future)'));\n  Future.microtask(() => print('3 — microtask queue (runs before events)'));\n\n  print('2 — sync (continues before any async)');\n\n  await Future.delayed(Duration.zero, () => print('5 — delayed event'));\n\n  \u002F\u002F Order: 1, 2, 3, 4, 5\n  \u002F\u002F Microtasks always drain completely before any event queue item runs.\n}\n\n\u002F\u002F ❌ Anti-pattern: using Future() for \"run after current frame\" in Flutter.\n\u002F\u002F Future() puts work on the event queue (next frame). For \"after current build,\"\n\u002F\u002F use WidgetsBinding.instance.addPostFrameCallback in Flutter, or\n\u002F\u002F scheduleMicrotask for \"after current sync execution.\"\n","",[29,30,31,40,46,52,58,65,86,102,107,131,155,160,172,177,210,215,221,227,233,238,244,250,256],"code",{"__ignoreMap":27},[32,33,36],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sdCPZ","\u002F\u002F Dart's event loop has TWO queues:\n",[32,41,43],{"class":34,"line":42},2,[32,44,45],{"class":38},"\u002F\u002F 1. Microtask queue — runs BEFORE event queue. For internal Dart bookkeeping.\n",[32,47,49],{"class":34,"line":48},3,[32,50,51],{"class":38},"\u002F\u002F    Use Future.microtask() for \"run ASAP, but after current sync code.\"\n",[32,53,55],{"class":34,"line":54},4,[32,56,57],{"class":38},"\u002F\u002F 2. Event queue — I\u002FO, timers, user events. Use Future() \u002F Future.delayed().\n",[32,59,61],{"class":34,"line":60},5,[32,62,64],{"emptyLinePlaceholder":63},true,"\n",[32,66,68,72,76,80,83],{"class":34,"line":67},6,[32,69,71],{"class":70},"svdQ7","void",[32,73,75],{"class":74},"sIsaT"," main",[32,77,79],{"class":78},"ssxIu","() ",[32,81,82],{"class":70},"async",[32,84,85],{"class":78}," {\n",[32,87,89,92,95,99],{"class":34,"line":88},7,[32,90,91],{"class":74},"  print",[32,93,94],{"class":78},"(",[32,96,98],{"class":97},"sJ6F3","'1 — sync'",[32,100,101],{"class":78},");\n",[32,103,105],{"class":34,"line":104},8,[32,106,64],{"emptyLinePlaceholder":63},[32,108,110,114,117,120,123,125,128],{"class":34,"line":109},9,[32,111,113],{"class":112},"snvgF","  Future",[32,115,116],{"class":78},"(() ",[32,118,119],{"class":70},"=>",[32,121,122],{"class":74}," print",[32,124,94],{"class":78},[32,126,127],{"class":97},"'4 — event queue (Future)'",[32,129,130],{"class":78},"));\n",[32,132,134,136,139,142,144,146,148,150,153],{"class":34,"line":133},10,[32,135,113],{"class":112},[32,137,138],{"class":78},".",[32,140,141],{"class":74},"microtask",[32,143,116],{"class":78},[32,145,119],{"class":70},[32,147,122],{"class":74},[32,149,94],{"class":78},[32,151,152],{"class":97},"'3 — microtask queue (runs before events)'",[32,154,130],{"class":78},[32,156,158],{"class":34,"line":157},11,[32,159,64],{"emptyLinePlaceholder":63},[32,161,163,165,167,170],{"class":34,"line":162},12,[32,164,91],{"class":74},[32,166,94],{"class":78},[32,168,169],{"class":97},"'2 — sync (continues before any async)'",[32,171,101],{"class":78},[32,173,175],{"class":34,"line":174},13,[32,176,64],{"emptyLinePlaceholder":63},[32,178,180,183,186,188,191,193,196,199,201,203,205,208],{"class":34,"line":179},14,[32,181,182],{"class":70},"  await",[32,184,185],{"class":112}," Future",[32,187,138],{"class":78},[32,189,190],{"class":74},"delayed",[32,192,94],{"class":78},[32,194,195],{"class":112},"Duration",[32,197,198],{"class":78},".zero, () ",[32,200,119],{"class":70},[32,202,122],{"class":74},[32,204,94],{"class":78},[32,206,207],{"class":97},"'5 — delayed event'",[32,209,130],{"class":78},[32,211,213],{"class":34,"line":212},15,[32,214,64],{"emptyLinePlaceholder":63},[32,216,218],{"class":34,"line":217},16,[32,219,220],{"class":38},"  \u002F\u002F Order: 1, 2, 3, 4, 5\n",[32,222,224],{"class":34,"line":223},17,[32,225,226],{"class":38},"  \u002F\u002F Microtasks always drain completely before any event queue item runs.\n",[32,228,230],{"class":34,"line":229},18,[32,231,232],{"class":78},"}\n",[32,234,236],{"class":34,"line":235},19,[32,237,64],{"emptyLinePlaceholder":63},[32,239,241],{"class":34,"line":240},20,[32,242,243],{"class":38},"\u002F\u002F ❌ Anti-pattern: using Future() for \"run after current frame\" in Flutter.\n",[32,245,247],{"class":34,"line":246},21,[32,248,249],{"class":38},"\u002F\u002F Future() puts work on the event queue (next frame). For \"after current build,\"\n",[32,251,253],{"class":34,"line":252},22,[32,254,255],{"class":38},"\u002F\u002F use WidgetsBinding.instance.addPostFrameCallback in Flutter, or\n",[32,257,259],{"class":34,"line":258},23,[32,260,261],{"class":38},"\u002F\u002F scheduleMicrotask for \"after current sync execution.\"\n",[14,263,265],{"id":264},"future-composition-parallel-vs-sequential","Future Composition — Parallel vs Sequential",[19,267,268],{"language":21},[23,269,271],{"className":25,"code":270,"language":21,"meta":27,"style":27},"\u002F\u002F ── Sequential: each await blocks until the previous completes. ──\n\u002F\u002F Total time = sum of all durations. Use when operations depend on each other.\nFuture\u003Cvoid> sequential() async {\n  var a = await fetch('url-a');  \u002F\u002F 1s\n  var b = await fetch('url-b');  \u002F\u002F 1s — starts after a completes\n  var c = await fetch('url-c');  \u002F\u002F 1s — starts after b completes\n  \u002F\u002F Total: 3s\n}\n\n\u002F\u002F ── Parallel: all futures start immediately, wait for all. ──\n\u002F\u002F Total time = max of all durations. Use for independent operations.\nFuture\u003Cvoid> parallel() async {\n  var results = await Future.wait([\n    fetch('url-a'),  \u002F\u002F starts immediately\n    fetch('url-b'),  \u002F\u002F starts immediately\n    fetch('url-c'),  \u002F\u002F starts immediately\n  ]);\n  \u002F\u002F Total: ~1s (the slowest one)\n}\n\n\u002F\u002F ── Future.wait preserves input order, regardless of completion order. ──\nFuture\u003CList\u003Cint>> getAll() async {\n  return Future.wait([\n    Future.delayed(Duration(seconds: 2), () => 1),  \u002F\u002F completes last\n    Future.delayed(Duration(seconds: 1), () => 2),  \u002F\u002F completes first\n  ]);\n  \u002F\u002F Returns [1, 2] — input order, NOT completion order.\n}\n\n\u002F\u002F ── Future.wait with eagerError (default: true) ──\n\u002F\u002F If any future fails, Future.wait completes with that error immediately\n\u002F\u002F (doesn't wait for the rest). Set eagerError: false to wait for all.\nFuture\u003Cvoid> tolerant() async {\n  try {\n    await Future.wait(\n      [fetch('a'), fetch('b'), fetch('c')],\n      eagerError: false,  \u002F\u002F don't short-circuit on first error\n    );\n  } catch (e) {\n    \u002F\u002F Catches the first error; other futures may still be running.\n  }\n}\n",[29,272,273,278,283,305,333,356,379,384,388,392,397,402,421,442,457,469,481,486,491,495,499,504,530,543,579,609,614,620,625,630,636,642,648,668,676,691,727,744,750,762,768,774],{"__ignoreMap":27},[32,274,275],{"class":34,"line":35},[32,276,277],{"class":38},"\u002F\u002F ── Sequential: each await blocks until the previous completes. ──\n",[32,279,280],{"class":34,"line":42},[32,281,282],{"class":38},"\u002F\u002F Total time = sum of all durations. Use when operations depend on each other.\n",[32,284,285,288,291,293,296,299,301,303],{"class":34,"line":48},[32,286,287],{"class":112},"Future",[32,289,290],{"class":78},"\u003C",[32,292,71],{"class":70},[32,294,295],{"class":78},"> ",[32,297,298],{"class":74},"sequential",[32,300,79],{"class":78},[32,302,82],{"class":70},[32,304,85],{"class":78},[32,306,307,310,313,316,319,322,324,327,330],{"class":34,"line":54},[32,308,309],{"class":70},"  var",[32,311,312],{"class":78}," a ",[32,314,315],{"class":70},"=",[32,317,318],{"class":70}," await",[32,320,321],{"class":74}," fetch",[32,323,94],{"class":78},[32,325,326],{"class":97},"'url-a'",[32,328,329],{"class":78},");  ",[32,331,332],{"class":38},"\u002F\u002F 1s\n",[32,334,335,337,340,342,344,346,348,351,353],{"class":34,"line":60},[32,336,309],{"class":70},[32,338,339],{"class":78}," b ",[32,341,315],{"class":70},[32,343,318],{"class":70},[32,345,321],{"class":74},[32,347,94],{"class":78},[32,349,350],{"class":97},"'url-b'",[32,352,329],{"class":78},[32,354,355],{"class":38},"\u002F\u002F 1s — starts after a completes\n",[32,357,358,360,363,365,367,369,371,374,376],{"class":34,"line":67},[32,359,309],{"class":70},[32,361,362],{"class":78}," c ",[32,364,315],{"class":70},[32,366,318],{"class":70},[32,368,321],{"class":74},[32,370,94],{"class":78},[32,372,373],{"class":97},"'url-c'",[32,375,329],{"class":78},[32,377,378],{"class":38},"\u002F\u002F 1s — starts after b completes\n",[32,380,381],{"class":34,"line":88},[32,382,383],{"class":38},"  \u002F\u002F Total: 3s\n",[32,385,386],{"class":34,"line":104},[32,387,232],{"class":78},[32,389,390],{"class":34,"line":109},[32,391,64],{"emptyLinePlaceholder":63},[32,393,394],{"class":34,"line":133},[32,395,396],{"class":38},"\u002F\u002F ── Parallel: all futures start immediately, wait for all. ──\n",[32,398,399],{"class":34,"line":157},[32,400,401],{"class":38},"\u002F\u002F Total time = max of all durations. Use for independent operations.\n",[32,403,404,406,408,410,412,415,417,419],{"class":34,"line":162},[32,405,287],{"class":112},[32,407,290],{"class":78},[32,409,71],{"class":70},[32,411,295],{"class":78},[32,413,414],{"class":74},"parallel",[32,416,79],{"class":78},[32,418,82],{"class":70},[32,420,85],{"class":78},[32,422,423,425,428,430,432,434,436,439],{"class":34,"line":174},[32,424,309],{"class":70},[32,426,427],{"class":78}," results ",[32,429,315],{"class":70},[32,431,318],{"class":70},[32,433,185],{"class":112},[32,435,138],{"class":78},[32,437,438],{"class":74},"wait",[32,440,441],{"class":78},"([\n",[32,443,444,447,449,451,454],{"class":34,"line":179},[32,445,446],{"class":74},"    fetch",[32,448,94],{"class":78},[32,450,326],{"class":97},[32,452,453],{"class":78},"),  ",[32,455,456],{"class":38},"\u002F\u002F starts immediately\n",[32,458,459,461,463,465,467],{"class":34,"line":212},[32,460,446],{"class":74},[32,462,94],{"class":78},[32,464,350],{"class":97},[32,466,453],{"class":78},[32,468,456],{"class":38},[32,470,471,473,475,477,479],{"class":34,"line":217},[32,472,446],{"class":74},[32,474,94],{"class":78},[32,476,373],{"class":97},[32,478,453],{"class":78},[32,480,456],{"class":38},[32,482,483],{"class":34,"line":223},[32,484,485],{"class":78},"  ]);\n",[32,487,488],{"class":34,"line":229},[32,489,490],{"class":38},"  \u002F\u002F Total: ~1s (the slowest one)\n",[32,492,493],{"class":34,"line":235},[32,494,232],{"class":78},[32,496,497],{"class":34,"line":240},[32,498,64],{"emptyLinePlaceholder":63},[32,500,501],{"class":34,"line":246},[32,502,503],{"class":38},"\u002F\u002F ── Future.wait preserves input order, regardless of completion order. ──\n",[32,505,506,508,510,513,515,518,521,524,526,528],{"class":34,"line":252},[32,507,287],{"class":112},[32,509,290],{"class":78},[32,511,512],{"class":112},"List",[32,514,290],{"class":78},[32,516,517],{"class":112},"int",[32,519,520],{"class":78},">> ",[32,522,523],{"class":74},"getAll",[32,525,79],{"class":78},[32,527,82],{"class":70},[32,529,85],{"class":78},[32,531,532,535,537,539,541],{"class":34,"line":258},[32,533,534],{"class":70},"  return",[32,536,185],{"class":112},[32,538,138],{"class":78},[32,540,438],{"class":74},[32,542,441],{"class":78},[32,544,546,549,551,553,555,557,560,563,566,569,571,574,576],{"class":34,"line":545},24,[32,547,548],{"class":112},"    Future",[32,550,138],{"class":78},[32,552,190],{"class":74},[32,554,94],{"class":78},[32,556,195],{"class":112},[32,558,559],{"class":78},"(seconds",[32,561,562],{"class":70},":",[32,564,565],{"class":112}," 2",[32,567,568],{"class":78},"), () ",[32,570,119],{"class":70},[32,572,573],{"class":112}," 1",[32,575,453],{"class":78},[32,577,578],{"class":38},"\u002F\u002F completes last\n",[32,580,582,584,586,588,590,592,594,596,598,600,602,604,606],{"class":34,"line":581},25,[32,583,548],{"class":112},[32,585,138],{"class":78},[32,587,190],{"class":74},[32,589,94],{"class":78},[32,591,195],{"class":112},[32,593,559],{"class":78},[32,595,562],{"class":70},[32,597,573],{"class":112},[32,599,568],{"class":78},[32,601,119],{"class":70},[32,603,565],{"class":112},[32,605,453],{"class":78},[32,607,608],{"class":38},"\u002F\u002F completes first\n",[32,610,612],{"class":34,"line":611},26,[32,613,485],{"class":78},[32,615,617],{"class":34,"line":616},27,[32,618,619],{"class":38},"  \u002F\u002F Returns [1, 2] — input order, NOT completion order.\n",[32,621,623],{"class":34,"line":622},28,[32,624,232],{"class":78},[32,626,628],{"class":34,"line":627},29,[32,629,64],{"emptyLinePlaceholder":63},[32,631,633],{"class":34,"line":632},30,[32,634,635],{"class":38},"\u002F\u002F ── Future.wait with eagerError (default: true) ──\n",[32,637,639],{"class":34,"line":638},31,[32,640,641],{"class":38},"\u002F\u002F If any future fails, Future.wait completes with that error immediately\n",[32,643,645],{"class":34,"line":644},32,[32,646,647],{"class":38},"\u002F\u002F (doesn't wait for the rest). Set eagerError: false to wait for all.\n",[32,649,651,653,655,657,659,662,664,666],{"class":34,"line":650},33,[32,652,287],{"class":112},[32,654,290],{"class":78},[32,656,71],{"class":70},[32,658,295],{"class":78},[32,660,661],{"class":74},"tolerant",[32,663,79],{"class":78},[32,665,82],{"class":70},[32,667,85],{"class":78},[32,669,671,674],{"class":34,"line":670},34,[32,672,673],{"class":70},"  try",[32,675,85],{"class":78},[32,677,679,682,684,686,688],{"class":34,"line":678},35,[32,680,681],{"class":70},"    await",[32,683,185],{"class":112},[32,685,138],{"class":78},[32,687,438],{"class":74},[32,689,690],{"class":78},"(\n",[32,692,694,697,700,702,705,708,710,712,715,717,719,721,724],{"class":34,"line":693},36,[32,695,696],{"class":78},"      [",[32,698,699],{"class":74},"fetch",[32,701,94],{"class":78},[32,703,704],{"class":97},"'a'",[32,706,707],{"class":78},"), ",[32,709,699],{"class":74},[32,711,94],{"class":78},[32,713,714],{"class":97},"'b'",[32,716,707],{"class":78},[32,718,699],{"class":74},[32,720,94],{"class":78},[32,722,723],{"class":97},"'c'",[32,725,726],{"class":78},")],\n",[32,728,730,733,735,738,741],{"class":34,"line":729},37,[32,731,732],{"class":78},"      eagerError",[32,734,562],{"class":70},[32,736,737],{"class":112}," false",[32,739,740],{"class":78},",  ",[32,742,743],{"class":38},"\u002F\u002F don't short-circuit on first error\n",[32,745,747],{"class":34,"line":746},38,[32,748,749],{"class":78},"    );\n",[32,751,753,756,759],{"class":34,"line":752},39,[32,754,755],{"class":78},"  } ",[32,757,758],{"class":70},"catch",[32,760,761],{"class":78}," (e) {\n",[32,763,765],{"class":34,"line":764},40,[32,766,767],{"class":38},"    \u002F\u002F Catches the first error; other futures may still be running.\n",[32,769,771],{"class":34,"line":770},41,[32,772,773],{"class":78},"  }\n",[32,775,777],{"class":34,"line":776},42,[32,778,232],{"class":78},[780,781,783],"h3",{"id":782},"concurrency-with-bounded-parallelism","Concurrency with Bounded Parallelism",[19,785,786],{"language":21},[23,787,789],{"className":25,"code":788,"language":21,"meta":27,"style":27},"\u002F\u002F ❌ Anti-pattern: Future.wait on 10,000 items — opens 10,000 connections.\nawait Future.wait(urls.map(fetch));  \u002F\u002F may exhaust connections\u002Fmemory\n\n\u002F\u002F ✓ Correct: bounded concurrency with a pool (package:pool).\nimport 'package:pool\u002Fpool.dart';\n\nfinal pool = Pool(10);  \u002F\u002F max 10 concurrent\nFuture\u003CList\u003CString>> fetchAll(List\u003CString> urls) {\n  return Future.wait(urls.map((url) => pool.withResource(() => fetch(url))));\n}\n\u002F\u002F Only 10 fetches run concurrently; the rest queue.\n\n\u002F\u002F Without a package — manual batching:\nFuture\u003CList\u003CString>> fetchBatched(List\u003CString> urls, {int batchSize = 10}) async {\n  final results = \u003CString>[];\n  for (var i = 0; i \u003C urls.length; i += batchSize) {\n    final batch = urls.skip(i).take(batchSize);\n    results.addAll(await Future.wait(batch.map(fetch)));\n  }\n  return results;\n}\n",[29,790,791,796,819,823,828,839,843,866,895,929,933,938,942,947,992,1012,1045,1070,1096,1100,1107],{"__ignoreMap":27},[32,792,793],{"class":34,"line":35},[32,794,795],{"class":38},"\u002F\u002F ❌ Anti-pattern: Future.wait on 10,000 items — opens 10,000 connections.\n",[32,797,798,801,803,805,807,810,813,816],{"class":34,"line":42},[32,799,800],{"class":70},"await",[32,802,185],{"class":112},[32,804,138],{"class":78},[32,806,438],{"class":74},[32,808,809],{"class":78},"(urls.",[32,811,812],{"class":74},"map",[32,814,815],{"class":78},"(fetch));  ",[32,817,818],{"class":38},"\u002F\u002F may exhaust connections\u002Fmemory\n",[32,820,821],{"class":34,"line":48},[32,822,64],{"emptyLinePlaceholder":63},[32,824,825],{"class":34,"line":54},[32,826,827],{"class":38},"\u002F\u002F ✓ Correct: bounded concurrency with a pool (package:pool).\n",[32,829,830,833,836],{"class":34,"line":60},[32,831,832],{"class":70},"import",[32,834,835],{"class":97}," 'package:pool\u002Fpool.dart'",[32,837,838],{"class":78},";\n",[32,840,841],{"class":34,"line":67},[32,842,64],{"emptyLinePlaceholder":63},[32,844,845,848,851,853,856,858,861,863],{"class":34,"line":88},[32,846,847],{"class":70},"final",[32,849,850],{"class":78}," pool ",[32,852,315],{"class":70},[32,854,855],{"class":112}," Pool",[32,857,94],{"class":78},[32,859,860],{"class":112},"10",[32,862,329],{"class":78},[32,864,865],{"class":38},"\u002F\u002F max 10 concurrent\n",[32,867,868,870,872,874,876,879,881,884,886,888,890,892],{"class":34,"line":104},[32,869,287],{"class":112},[32,871,290],{"class":78},[32,873,512],{"class":112},[32,875,290],{"class":78},[32,877,878],{"class":112},"String",[32,880,520],{"class":78},[32,882,883],{"class":74},"fetchAll",[32,885,94],{"class":78},[32,887,512],{"class":112},[32,889,290],{"class":78},[32,891,878],{"class":112},[32,893,894],{"class":78},"> urls) {\n",[32,896,897,899,901,903,905,907,909,912,914,917,920,922,924,926],{"class":34,"line":109},[32,898,534],{"class":70},[32,900,185],{"class":112},[32,902,138],{"class":78},[32,904,438],{"class":74},[32,906,809],{"class":78},[32,908,812],{"class":74},[32,910,911],{"class":78},"((url) ",[32,913,119],{"class":70},[32,915,916],{"class":78}," pool.",[32,918,919],{"class":74},"withResource",[32,921,116],{"class":78},[32,923,119],{"class":70},[32,925,321],{"class":74},[32,927,928],{"class":78},"(url))));\n",[32,930,931],{"class":34,"line":133},[32,932,232],{"class":78},[32,934,935],{"class":34,"line":157},[32,936,937],{"class":38},"\u002F\u002F Only 10 fetches run concurrently; the rest queue.\n",[32,939,940],{"class":34,"line":162},[32,941,64],{"emptyLinePlaceholder":63},[32,943,944],{"class":34,"line":174},[32,945,946],{"class":38},"\u002F\u002F Without a package — manual batching:\n",[32,948,949,951,953,955,957,959,961,964,966,968,970,972,975,977,980,982,985,988,990],{"class":34,"line":179},[32,950,287],{"class":112},[32,952,290],{"class":78},[32,954,512],{"class":112},[32,956,290],{"class":78},[32,958,878],{"class":112},[32,960,520],{"class":78},[32,962,963],{"class":74},"fetchBatched",[32,965,94],{"class":78},[32,967,512],{"class":112},[32,969,290],{"class":78},[32,971,878],{"class":112},[32,973,974],{"class":78},"> urls, {",[32,976,517],{"class":112},[32,978,979],{"class":78}," batchSize ",[32,981,315],{"class":70},[32,983,984],{"class":112}," 10",[32,986,987],{"class":78},"}) ",[32,989,82],{"class":70},[32,991,85],{"class":78},[32,993,994,997,999,1001,1004,1006,1009],{"class":34,"line":212},[32,995,996],{"class":70},"  final",[32,998,427],{"class":78},[32,1000,315],{"class":70},[32,1002,1003],{"class":70}," \u003C",[32,1005,878],{"class":112},[32,1007,1008],{"class":70},">",[32,1010,1011],{"class":78},"[];\n",[32,1013,1014,1017,1020,1023,1026,1028,1031,1034,1036,1039,1042],{"class":34,"line":217},[32,1015,1016],{"class":70},"  for",[32,1018,1019],{"class":78}," (",[32,1021,1022],{"class":70},"var",[32,1024,1025],{"class":78}," i ",[32,1027,315],{"class":70},[32,1029,1030],{"class":112}," 0",[32,1032,1033],{"class":78},"; i ",[32,1035,290],{"class":70},[32,1037,1038],{"class":78}," urls.length; i ",[32,1040,1041],{"class":70},"+=",[32,1043,1044],{"class":78}," batchSize) {\n",[32,1046,1047,1050,1053,1055,1058,1061,1064,1067],{"class":34,"line":223},[32,1048,1049],{"class":70},"    final",[32,1051,1052],{"class":78}," batch ",[32,1054,315],{"class":70},[32,1056,1057],{"class":78}," urls.",[32,1059,1060],{"class":74},"skip",[32,1062,1063],{"class":78},"(i).",[32,1065,1066],{"class":74},"take",[32,1068,1069],{"class":78},"(batchSize);\n",[32,1071,1072,1075,1078,1080,1082,1084,1086,1088,1091,1093],{"class":34,"line":229},[32,1073,1074],{"class":78},"    results.",[32,1076,1077],{"class":74},"addAll",[32,1079,94],{"class":78},[32,1081,800],{"class":70},[32,1083,185],{"class":112},[32,1085,138],{"class":78},[32,1087,438],{"class":74},[32,1089,1090],{"class":78},"(batch.",[32,1092,812],{"class":74},[32,1094,1095],{"class":78},"(fetch)));\n",[32,1097,1098],{"class":34,"line":235},[32,1099,773],{"class":78},[32,1101,1102,1104],{"class":34,"line":240},[32,1103,534],{"class":70},[32,1105,1106],{"class":78}," results;\n",[32,1108,1109],{"class":34,"line":246},[32,1110,232],{"class":78},[14,1112,1114],{"id":1113},"stream-pipelines-transformation-backpressure","Stream Pipelines — Transformation & Backpressure",[19,1116,1117],{"language":21},[23,1118,1120],{"className":25,"code":1119,"language":21,"meta":27,"style":27},"\u002F\u002F Streams are async sequences. Single-subscription (default) or broadcast.\n\u002F\u002F Transformations are lazy — they run only when listened to.\n\n\u002F\u002F ── Transform: map, where, take, skip, debounce ──\nStream\u003CString> searchStream = searchTextController.stream\n    .where((text) => text.length > 2)      \u002F\u002F filter\n    .debounceTime(Duration(milliseconds: 300))  \u002F\u002F from rxdart\n    .distinct()                            \u002F\u002F skip consecutive duplicates\n    .map((text) => text.trim())\n    .asyncMap((text) => api.search(text));  \u002F\u002F async transform (returns Future)\n\n\u002F\u002F ── asyncMap vs map ──\n\u002F\u002F map: sync transform — 1:1, immediate.\n\u002F\u002F asyncMap: async transform — awaits each Future, emits the result.\n\u002F\u002F           The stream pauses until the Future completes (backpressure).\nStream\u003Cint> asyncTransform(Stream\u003Cint> input) =>\n    input.asyncMap((n) async {\n      await Future.delayed(Duration(milliseconds: 100));\n      return n * 2;\n    });\n\n\u002F\u002F ── Handling errors in a stream ──\nStream\u003Cint> numbers = Stream.fromIterable([1, 2, 0, 4])\n    .map((n) => 10 ~\u002F n)  \u002F\u002F throws on n=0 (division by zero)\n    .handleError((error) {\n      \u002F\u002F handleError catches errors mid-stream — the stream continues.\n      print('Error: $error');\n    });\n\u002F\u002F Emits: 10, 5, (error: IntegerDivisionByZeroException), 2\n\n\u002F\u002F ── Single-subscription vs broadcast ──\n\u002F\u002F Single-subscription (default): one listener. Listening twice throws.\n\u002F\u002F Broadcast: multiple listeners. No buffering (late listeners miss events).\nvar singleSub = Stream.fromIterable([1, 2, 3]);\nsingleSub.listen(print);\n\u002F\u002F singleSub.listen(print);  \u002F\u002F ✗ StateError: stream has already been listened to\n\nvar broadcast = singleSub.asBroadcastStream();\nbroadcast.listen(print);\nbroadcast.listen(print);  \u002F\u002F ✓ both listeners receive events\n",[29,1121,1122,1127,1132,1136,1141,1158,1184,1209,1222,1241,1264,1268,1273,1278,1283,1288,1315,1329,1353,1368,1373,1377,1382,1428,1449,1459,1464,1485,1489,1494,1498,1503,1508,1513,1544,1555,1563,1567,1585,1594],{"__ignoreMap":27},[32,1123,1124],{"class":34,"line":35},[32,1125,1126],{"class":38},"\u002F\u002F Streams are async sequences. Single-subscription (default) or broadcast.\n",[32,1128,1129],{"class":34,"line":42},[32,1130,1131],{"class":38},"\u002F\u002F Transformations are lazy — they run only when listened to.\n",[32,1133,1134],{"class":34,"line":48},[32,1135,64],{"emptyLinePlaceholder":63},[32,1137,1138],{"class":34,"line":54},[32,1139,1140],{"class":38},"\u002F\u002F ── Transform: map, where, take, skip, debounce ──\n",[32,1142,1143,1146,1148,1150,1153,1155],{"class":34,"line":60},[32,1144,1145],{"class":112},"Stream",[32,1147,290],{"class":78},[32,1149,878],{"class":112},[32,1151,1152],{"class":78},"> searchStream ",[32,1154,315],{"class":70},[32,1156,1157],{"class":78}," searchTextController.stream\n",[32,1159,1160,1163,1166,1169,1171,1174,1176,1178,1181],{"class":34,"line":67},[32,1161,1162],{"class":78},"    .",[32,1164,1165],{"class":74},"where",[32,1167,1168],{"class":78},"((text) ",[32,1170,119],{"class":70},[32,1172,1173],{"class":78}," text.length ",[32,1175,1008],{"class":70},[32,1177,565],{"class":112},[32,1179,1180],{"class":78},")      ",[32,1182,1183],{"class":38},"\u002F\u002F filter\n",[32,1185,1186,1188,1191,1193,1195,1198,1200,1203,1206],{"class":34,"line":88},[32,1187,1162],{"class":78},[32,1189,1190],{"class":74},"debounceTime",[32,1192,94],{"class":78},[32,1194,195],{"class":112},[32,1196,1197],{"class":78},"(milliseconds",[32,1199,562],{"class":70},[32,1201,1202],{"class":112}," 300",[32,1204,1205],{"class":78},"))  ",[32,1207,1208],{"class":38},"\u002F\u002F from rxdart\n",[32,1210,1211,1213,1216,1219],{"class":34,"line":104},[32,1212,1162],{"class":78},[32,1214,1215],{"class":74},"distinct",[32,1217,1218],{"class":78},"()                            ",[32,1220,1221],{"class":38},"\u002F\u002F skip consecutive duplicates\n",[32,1223,1224,1226,1228,1230,1232,1235,1238],{"class":34,"line":109},[32,1225,1162],{"class":78},[32,1227,812],{"class":74},[32,1229,1168],{"class":78},[32,1231,119],{"class":70},[32,1233,1234],{"class":78}," text.",[32,1236,1237],{"class":74},"trim",[32,1239,1240],{"class":78},"())\n",[32,1242,1243,1245,1248,1250,1252,1255,1258,1261],{"class":34,"line":133},[32,1244,1162],{"class":78},[32,1246,1247],{"class":74},"asyncMap",[32,1249,1168],{"class":78},[32,1251,119],{"class":70},[32,1253,1254],{"class":78}," api.",[32,1256,1257],{"class":74},"search",[32,1259,1260],{"class":78},"(text));  ",[32,1262,1263],{"class":38},"\u002F\u002F async transform (returns Future)\n",[32,1265,1266],{"class":34,"line":157},[32,1267,64],{"emptyLinePlaceholder":63},[32,1269,1270],{"class":34,"line":162},[32,1271,1272],{"class":38},"\u002F\u002F ── asyncMap vs map ──\n",[32,1274,1275],{"class":34,"line":174},[32,1276,1277],{"class":38},"\u002F\u002F map: sync transform — 1:1, immediate.\n",[32,1279,1280],{"class":34,"line":179},[32,1281,1282],{"class":38},"\u002F\u002F asyncMap: async transform — awaits each Future, emits the result.\n",[32,1284,1285],{"class":34,"line":212},[32,1286,1287],{"class":38},"\u002F\u002F           The stream pauses until the Future completes (backpressure).\n",[32,1289,1290,1292,1294,1296,1298,1301,1303,1305,1307,1309,1312],{"class":34,"line":217},[32,1291,1145],{"class":112},[32,1293,290],{"class":78},[32,1295,517],{"class":112},[32,1297,295],{"class":78},[32,1299,1300],{"class":74},"asyncTransform",[32,1302,94],{"class":78},[32,1304,1145],{"class":112},[32,1306,290],{"class":78},[32,1308,517],{"class":112},[32,1310,1311],{"class":78},"> input) ",[32,1313,1314],{"class":70},"=>\n",[32,1316,1317,1320,1322,1325,1327],{"class":34,"line":223},[32,1318,1319],{"class":78},"    input.",[32,1321,1247],{"class":74},[32,1323,1324],{"class":78},"((n) ",[32,1326,82],{"class":70},[32,1328,85],{"class":78},[32,1330,1331,1334,1336,1338,1340,1342,1344,1346,1348,1351],{"class":34,"line":229},[32,1332,1333],{"class":70},"      await",[32,1335,185],{"class":112},[32,1337,138],{"class":78},[32,1339,190],{"class":74},[32,1341,94],{"class":78},[32,1343,195],{"class":112},[32,1345,1197],{"class":78},[32,1347,562],{"class":70},[32,1349,1350],{"class":112}," 100",[32,1352,130],{"class":78},[32,1354,1355,1358,1361,1364,1366],{"class":34,"line":235},[32,1356,1357],{"class":70},"      return",[32,1359,1360],{"class":78}," n ",[32,1362,1363],{"class":70},"*",[32,1365,565],{"class":112},[32,1367,838],{"class":78},[32,1369,1370],{"class":34,"line":240},[32,1371,1372],{"class":78},"    });\n",[32,1374,1375],{"class":34,"line":246},[32,1376,64],{"emptyLinePlaceholder":63},[32,1378,1379],{"class":34,"line":252},[32,1380,1381],{"class":38},"\u002F\u002F ── Handling errors in a stream ──\n",[32,1383,1384,1386,1388,1390,1393,1395,1398,1400,1403,1406,1409,1412,1415,1417,1420,1422,1425],{"class":34,"line":258},[32,1385,1145],{"class":112},[32,1387,290],{"class":78},[32,1389,517],{"class":112},[32,1391,1392],{"class":78},"> numbers ",[32,1394,315],{"class":70},[32,1396,1397],{"class":112}," Stream",[32,1399,138],{"class":78},[32,1401,1402],{"class":74},"fromIterable",[32,1404,1405],{"class":78},"([",[32,1407,1408],{"class":112},"1",[32,1410,1411],{"class":78},", ",[32,1413,1414],{"class":112},"2",[32,1416,1411],{"class":78},[32,1418,1419],{"class":112},"0",[32,1421,1411],{"class":78},[32,1423,1424],{"class":112},"4",[32,1426,1427],{"class":78},"])\n",[32,1429,1430,1432,1434,1436,1438,1440,1443,1446],{"class":34,"line":545},[32,1431,1162],{"class":78},[32,1433,812],{"class":74},[32,1435,1324],{"class":78},[32,1437,119],{"class":70},[32,1439,984],{"class":112},[32,1441,1442],{"class":70}," ~\u002F",[32,1444,1445],{"class":78}," n)  ",[32,1447,1448],{"class":38},"\u002F\u002F throws on n=0 (division by zero)\n",[32,1450,1451,1453,1456],{"class":34,"line":581},[32,1452,1162],{"class":78},[32,1454,1455],{"class":74},"handleError",[32,1457,1458],{"class":78},"((error) {\n",[32,1460,1461],{"class":34,"line":611},[32,1462,1463],{"class":38},"      \u002F\u002F handleError catches errors mid-stream — the stream continues.\n",[32,1465,1466,1469,1471,1474,1477,1480,1483],{"class":34,"line":616},[32,1467,1468],{"class":74},"      print",[32,1470,94],{"class":78},[32,1472,1473],{"class":97},"'Error: ",[32,1475,1476],{"class":97},"$",[32,1478,1479],{"class":112},"error",[32,1481,1482],{"class":97},"'",[32,1484,101],{"class":78},[32,1486,1487],{"class":34,"line":622},[32,1488,1372],{"class":78},[32,1490,1491],{"class":34,"line":627},[32,1492,1493],{"class":38},"\u002F\u002F Emits: 10, 5, (error: IntegerDivisionByZeroException), 2\n",[32,1495,1496],{"class":34,"line":632},[32,1497,64],{"emptyLinePlaceholder":63},[32,1499,1500],{"class":34,"line":638},[32,1501,1502],{"class":38},"\u002F\u002F ── Single-subscription vs broadcast ──\n",[32,1504,1505],{"class":34,"line":644},[32,1506,1507],{"class":38},"\u002F\u002F Single-subscription (default): one listener. Listening twice throws.\n",[32,1509,1510],{"class":34,"line":650},[32,1511,1512],{"class":38},"\u002F\u002F Broadcast: multiple listeners. No buffering (late listeners miss events).\n",[32,1514,1515,1517,1520,1522,1524,1526,1528,1530,1532,1534,1536,1538,1541],{"class":34,"line":670},[32,1516,1022],{"class":70},[32,1518,1519],{"class":78}," singleSub ",[32,1521,315],{"class":70},[32,1523,1397],{"class":112},[32,1525,138],{"class":78},[32,1527,1402],{"class":74},[32,1529,1405],{"class":78},[32,1531,1408],{"class":112},[32,1533,1411],{"class":78},[32,1535,1414],{"class":112},[32,1537,1411],{"class":78},[32,1539,1540],{"class":112},"3",[32,1542,1543],{"class":78},"]);\n",[32,1545,1546,1549,1552],{"class":34,"line":678},[32,1547,1548],{"class":78},"singleSub.",[32,1550,1551],{"class":74},"listen",[32,1553,1554],{"class":78},"(print);\n",[32,1556,1557,1560],{"class":34,"line":693},[32,1558,1559],{"class":38},"\u002F\u002F singleSub.listen(print);",[32,1561,1562],{"class":38},"  \u002F\u002F ✗ StateError: stream has already been listened to\n",[32,1564,1565],{"class":34,"line":729},[32,1566,64],{"emptyLinePlaceholder":63},[32,1568,1569,1571,1574,1576,1579,1582],{"class":34,"line":746},[32,1570,1022],{"class":70},[32,1572,1573],{"class":78}," broadcast ",[32,1575,315],{"class":70},[32,1577,1578],{"class":78}," singleSub.",[32,1580,1581],{"class":74},"asBroadcastStream",[32,1583,1584],{"class":78},"();\n",[32,1586,1587,1590,1592],{"class":34,"line":752},[32,1588,1589],{"class":78},"broadcast.",[32,1591,1551],{"class":74},[32,1593,1554],{"class":78},[32,1595,1596,1598,1600,1603],{"class":34,"line":764},[32,1597,1589],{"class":78},[32,1599,1551],{"class":74},[32,1601,1602],{"class":78},"(print);  ",[32,1604,1605],{"class":38},"\u002F\u002F ✓ both listeners receive events\n",[14,1607,1609],{"id":1608},"streamcontroller-building-custom-streams","StreamController — Building Custom Streams",[19,1611,1612],{"language":21},[23,1613,1615],{"className":25,"code":1614,"language":21,"meta":27,"style":27},"import 'dart:async';\n\n\u002F\u002F StreamController: manually add events to a stream.\n\u002F\u002F Use for bridging callback-based APIs to streams, or building event sources.\n\nclass EventBus {\n  final _controller = StreamController\u003CEvent>.broadcast();  \u002F\u002F multi-listener\n  Stream\u003CEvent> get stream => _controller.stream;\n\n  void emit(Event event) => _controller.add(event);\n  void emitError(Object error) => _controller.addError(error);\n  void close() => _controller.close();\n}\n\n\u002F\u002F ❌ Anti-pattern: forgetting to close the controller → resource leak.\n\u002F\u002F Always close in dispose()\u002Fclose() methods.\n\n\u002F\u002F ── Bridge: callback API to Stream ──\nclass MouseTracker {\n  final _controller = StreamController\u003COffset>();\n  Stream\u003COffset> get positions => _controller.stream;\n\n  void onMouseMove(Offset pos) => _controller.add(pos);\n\n  void dispose() {\n    _controller.close();  \u002F\u002F ← MUST close — listeners stop, resources freed\n  }\n}\n\n\u002F\u002F ── StreamController with pause\u002Fresume support ──\n\u002F\u002F Single-subscription controllers support pause\u002Fresume (broadcast don't buffer).\nvar controller = StreamController\u003Cint>(\n  onPause: () => print('Paused'),   \u002F\u002F called when listener pauses\n  onResume: () => print('Resumed'), \u002F\u002F called when listener resumes\n  onCancel: () => print('Cancelled'), \u002F\u002F called when listener cancels\n  sync: true,  \u002F\u002F synchronous delivery (no async scheduling — use carefully)\n);\n",[29,1616,1617,1626,1630,1635,1640,1644,1654,1683,1705,1709,1735,1760,1778,1782,1786,1791,1796,1800,1805,1814,1832,1851,1855,1878,1882,1892,1904,1908,1912,1916,1921,1926,1944,1969,1992,2015,2028],{"__ignoreMap":27},[32,1618,1619,1621,1624],{"class":34,"line":35},[32,1620,832],{"class":70},[32,1622,1623],{"class":97}," 'dart:async'",[32,1625,838],{"class":78},[32,1627,1628],{"class":34,"line":42},[32,1629,64],{"emptyLinePlaceholder":63},[32,1631,1632],{"class":34,"line":48},[32,1633,1634],{"class":38},"\u002F\u002F StreamController: manually add events to a stream.\n",[32,1636,1637],{"class":34,"line":54},[32,1638,1639],{"class":38},"\u002F\u002F Use for bridging callback-based APIs to streams, or building event sources.\n",[32,1641,1642],{"class":34,"line":60},[32,1643,64],{"emptyLinePlaceholder":63},[32,1645,1646,1649,1652],{"class":34,"line":67},[32,1647,1648],{"class":70},"class",[32,1650,1651],{"class":112}," EventBus",[32,1653,85],{"class":78},[32,1655,1656,1658,1661,1663,1666,1668,1671,1674,1677,1680],{"class":34,"line":88},[32,1657,996],{"class":70},[32,1659,1660],{"class":78}," _controller ",[32,1662,315],{"class":70},[32,1664,1665],{"class":112}," StreamController",[32,1667,290],{"class":78},[32,1669,1670],{"class":112},"Event",[32,1672,1673],{"class":78},">.",[32,1675,1676],{"class":74},"broadcast",[32,1678,1679],{"class":78},"();  ",[32,1681,1682],{"class":38},"\u002F\u002F multi-listener\n",[32,1684,1685,1688,1690,1692,1694,1697,1700,1702],{"class":34,"line":104},[32,1686,1687],{"class":112},"  Stream",[32,1689,290],{"class":78},[32,1691,1670],{"class":112},[32,1693,295],{"class":78},[32,1695,1696],{"class":70},"get",[32,1698,1699],{"class":78}," stream ",[32,1701,119],{"class":70},[32,1703,1704],{"class":78}," _controller.stream;\n",[32,1706,1707],{"class":34,"line":109},[32,1708,64],{"emptyLinePlaceholder":63},[32,1710,1711,1714,1717,1719,1721,1724,1726,1729,1732],{"class":34,"line":133},[32,1712,1713],{"class":70},"  void",[32,1715,1716],{"class":74}," emit",[32,1718,94],{"class":78},[32,1720,1670],{"class":112},[32,1722,1723],{"class":78}," event) ",[32,1725,119],{"class":70},[32,1727,1728],{"class":78}," _controller.",[32,1730,1731],{"class":74},"add",[32,1733,1734],{"class":78},"(event);\n",[32,1736,1737,1739,1742,1744,1747,1750,1752,1754,1757],{"class":34,"line":157},[32,1738,1713],{"class":70},[32,1740,1741],{"class":74}," emitError",[32,1743,94],{"class":78},[32,1745,1746],{"class":112},"Object",[32,1748,1749],{"class":78}," error) ",[32,1751,119],{"class":70},[32,1753,1728],{"class":78},[32,1755,1756],{"class":74},"addError",[32,1758,1759],{"class":78},"(error);\n",[32,1761,1762,1764,1767,1769,1771,1773,1776],{"class":34,"line":162},[32,1763,1713],{"class":70},[32,1765,1766],{"class":74}," close",[32,1768,79],{"class":78},[32,1770,119],{"class":70},[32,1772,1728],{"class":78},[32,1774,1775],{"class":74},"close",[32,1777,1584],{"class":78},[32,1779,1780],{"class":34,"line":174},[32,1781,232],{"class":78},[32,1783,1784],{"class":34,"line":179},[32,1785,64],{"emptyLinePlaceholder":63},[32,1787,1788],{"class":34,"line":212},[32,1789,1790],{"class":38},"\u002F\u002F ❌ Anti-pattern: forgetting to close the controller → resource leak.\n",[32,1792,1793],{"class":34,"line":217},[32,1794,1795],{"class":38},"\u002F\u002F Always close in dispose()\u002Fclose() methods.\n",[32,1797,1798],{"class":34,"line":223},[32,1799,64],{"emptyLinePlaceholder":63},[32,1801,1802],{"class":34,"line":229},[32,1803,1804],{"class":38},"\u002F\u002F ── Bridge: callback API to Stream ──\n",[32,1806,1807,1809,1812],{"class":34,"line":235},[32,1808,1648],{"class":70},[32,1810,1811],{"class":112}," MouseTracker",[32,1813,85],{"class":78},[32,1815,1816,1818,1820,1822,1824,1826,1829],{"class":34,"line":240},[32,1817,996],{"class":70},[32,1819,1660],{"class":78},[32,1821,315],{"class":70},[32,1823,1665],{"class":112},[32,1825,290],{"class":78},[32,1827,1828],{"class":112},"Offset",[32,1830,1831],{"class":78},">();\n",[32,1833,1834,1836,1838,1840,1842,1844,1847,1849],{"class":34,"line":246},[32,1835,1687],{"class":112},[32,1837,290],{"class":78},[32,1839,1828],{"class":112},[32,1841,295],{"class":78},[32,1843,1696],{"class":70},[32,1845,1846],{"class":78}," positions ",[32,1848,119],{"class":70},[32,1850,1704],{"class":78},[32,1852,1853],{"class":34,"line":252},[32,1854,64],{"emptyLinePlaceholder":63},[32,1856,1857,1859,1862,1864,1866,1869,1871,1873,1875],{"class":34,"line":258},[32,1858,1713],{"class":70},[32,1860,1861],{"class":74}," onMouseMove",[32,1863,94],{"class":78},[32,1865,1828],{"class":112},[32,1867,1868],{"class":78}," pos) ",[32,1870,119],{"class":70},[32,1872,1728],{"class":78},[32,1874,1731],{"class":74},[32,1876,1877],{"class":78},"(pos);\n",[32,1879,1880],{"class":34,"line":545},[32,1881,64],{"emptyLinePlaceholder":63},[32,1883,1884,1886,1889],{"class":34,"line":581},[32,1885,1713],{"class":70},[32,1887,1888],{"class":74}," dispose",[32,1890,1891],{"class":78},"() {\n",[32,1893,1894,1897,1899,1901],{"class":34,"line":611},[32,1895,1896],{"class":78},"    _controller.",[32,1898,1775],{"class":74},[32,1900,1679],{"class":78},[32,1902,1903],{"class":38},"\u002F\u002F ← MUST close — listeners stop, resources freed\n",[32,1905,1906],{"class":34,"line":616},[32,1907,773],{"class":78},[32,1909,1910],{"class":34,"line":622},[32,1911,232],{"class":78},[32,1913,1914],{"class":34,"line":627},[32,1915,64],{"emptyLinePlaceholder":63},[32,1917,1918],{"class":34,"line":632},[32,1919,1920],{"class":38},"\u002F\u002F ── StreamController with pause\u002Fresume support ──\n",[32,1922,1923],{"class":34,"line":638},[32,1924,1925],{"class":38},"\u002F\u002F Single-subscription controllers support pause\u002Fresume (broadcast don't buffer).\n",[32,1927,1928,1930,1933,1935,1937,1939,1941],{"class":34,"line":644},[32,1929,1022],{"class":70},[32,1931,1932],{"class":78}," controller ",[32,1934,315],{"class":70},[32,1936,1665],{"class":112},[32,1938,290],{"class":78},[32,1940,517],{"class":112},[32,1942,1943],{"class":78},">(\n",[32,1945,1946,1949,1951,1954,1956,1958,1960,1963,1966],{"class":34,"line":650},[32,1947,1948],{"class":78},"  onPause",[32,1950,562],{"class":70},[32,1952,1953],{"class":78}," () ",[32,1955,119],{"class":70},[32,1957,122],{"class":74},[32,1959,94],{"class":78},[32,1961,1962],{"class":97},"'Paused'",[32,1964,1965],{"class":78},"),   ",[32,1967,1968],{"class":38},"\u002F\u002F called when listener pauses\n",[32,1970,1971,1974,1976,1978,1980,1982,1984,1987,1989],{"class":34,"line":670},[32,1972,1973],{"class":78},"  onResume",[32,1975,562],{"class":70},[32,1977,1953],{"class":78},[32,1979,119],{"class":70},[32,1981,122],{"class":74},[32,1983,94],{"class":78},[32,1985,1986],{"class":97},"'Resumed'",[32,1988,707],{"class":78},[32,1990,1991],{"class":38},"\u002F\u002F called when listener resumes\n",[32,1993,1994,1997,1999,2001,2003,2005,2007,2010,2012],{"class":34,"line":678},[32,1995,1996],{"class":78},"  onCancel",[32,1998,562],{"class":70},[32,2000,1953],{"class":78},[32,2002,119],{"class":70},[32,2004,122],{"class":74},[32,2006,94],{"class":78},[32,2008,2009],{"class":97},"'Cancelled'",[32,2011,707],{"class":78},[32,2013,2014],{"class":38},"\u002F\u002F called when listener cancels\n",[32,2016,2017,2020,2023,2025],{"class":34,"line":693},[32,2018,2019],{"class":70},"  sync:",[32,2021,2022],{"class":112}," true",[32,2024,740],{"class":78},[32,2026,2027],{"class":38},"\u002F\u002F synchronous delivery (no async scheduling — use carefully)\n",[32,2029,2030],{"class":34,"line":729},[32,2031,101],{"class":78},[14,2033,2035,2038],{"id":2034},"async-generators-lazy-stream-production",[29,2036,2037],{},"async*"," Generators — Lazy Stream Production",[19,2040,2041],{"language":21},[23,2042,2044],{"className":25,"code":2043,"language":21,"meta":27,"style":27},"\u002F\u002F async* returns a Stream. yield emits a value, yield* delegates to another stream.\n\u002F\u002F The generator suspends at each yield and resumes when the listener pulls.\n\nStream\u003Cint> intervalCounter(Duration interval, {int? max}) async* {\n  var i = 0;\n  while (max == null || i \u003C max) {\n    await Future.delayed(interval);\n    yield i++;\n  }\n}\n\n\u002F\u002F Consuming with await for (sequential, blocks until stream closes):\nvoid main() async {\n  await for (final n in intervalCounter(Duration(seconds: 1), max: 3)) {\n    print(n);  \u002F\u002F 0 (after 1s), 1 (after 2s), 2 (after 3s)\n  }\n  print('Done');  \u002F\u002F after 3s, when the stream closes\n}\n\n\u002F\u002F Consuming with listen (non-blocking, can cancel):\nvoid listenExample() {\n  final sub = intervalCounter(Duration(seconds: 1)).listen(\n    (n) => print(n),\n    onError: (e) => print('Error: $e'),\n    onDone: () => print('Done'),\n  );\n\n  \u002F\u002F Cancel after 5 seconds:\n  Future.delayed(Duration(seconds: 5), sub.cancel);\n}\n\n\u002F\u002F yield* delegates to another stream (flattens):\nStream\u003Cint> merged() async* {\n  yield* intervalCounter(Duration(seconds: 1), max: 3);  \u002F\u002F 0, 1, 2\n  yield* intervalCounter(Duration(seconds: 2), max: 2);  \u002F\u002F 0, 1 (slower)\n}\n",[29,2045,2046,2051,2056,2060,2092,2104,2128,2141,2154,2158,2162,2166,2171,2183,2223,2234,2238,2252,2256,2260,2265,2274,2302,2314,2342,2361,2366,2370,2375,2397,2401,2405,2410,2429,2457,2484],{"__ignoreMap":27},[32,2047,2048],{"class":34,"line":35},[32,2049,2050],{"class":38},"\u002F\u002F async* returns a Stream. yield emits a value, yield* delegates to another stream.\n",[32,2052,2053],{"class":34,"line":42},[32,2054,2055],{"class":38},"\u002F\u002F The generator suspends at each yield and resumes when the listener pulls.\n",[32,2057,2058],{"class":34,"line":48},[32,2059,64],{"emptyLinePlaceholder":63},[32,2061,2062,2064,2066,2068,2070,2073,2075,2077,2080,2082,2085,2088,2090],{"class":34,"line":54},[32,2063,1145],{"class":112},[32,2065,290],{"class":78},[32,2067,517],{"class":112},[32,2069,295],{"class":78},[32,2071,2072],{"class":74},"intervalCounter",[32,2074,94],{"class":78},[32,2076,195],{"class":112},[32,2078,2079],{"class":78}," interval, {",[32,2081,517],{"class":112},[32,2083,2084],{"class":70},"?",[32,2086,2087],{"class":78}," max}) ",[32,2089,2037],{"class":70},[32,2091,85],{"class":78},[32,2093,2094,2096,2098,2100,2102],{"class":34,"line":60},[32,2095,309],{"class":70},[32,2097,1025],{"class":78},[32,2099,315],{"class":70},[32,2101,1030],{"class":112},[32,2103,838],{"class":78},[32,2105,2106,2109,2112,2115,2118,2121,2123,2125],{"class":34,"line":67},[32,2107,2108],{"class":70},"  while",[32,2110,2111],{"class":78}," (max ",[32,2113,2114],{"class":70},"==",[32,2116,2117],{"class":112}," null",[32,2119,2120],{"class":70}," ||",[32,2122,1025],{"class":78},[32,2124,290],{"class":70},[32,2126,2127],{"class":78}," max) {\n",[32,2129,2130,2132,2134,2136,2138],{"class":34,"line":88},[32,2131,681],{"class":70},[32,2133,185],{"class":112},[32,2135,138],{"class":78},[32,2137,190],{"class":74},[32,2139,2140],{"class":78},"(interval);\n",[32,2142,2143,2146,2149,2152],{"class":34,"line":104},[32,2144,2145],{"class":70},"    yield",[32,2147,2148],{"class":78}," i",[32,2150,2151],{"class":70},"++",[32,2153,838],{"class":78},[32,2155,2156],{"class":34,"line":109},[32,2157,773],{"class":78},[32,2159,2160],{"class":34,"line":133},[32,2161,232],{"class":78},[32,2163,2164],{"class":34,"line":157},[32,2165,64],{"emptyLinePlaceholder":63},[32,2167,2168],{"class":34,"line":162},[32,2169,2170],{"class":38},"\u002F\u002F Consuming with await for (sequential, blocks until stream closes):\n",[32,2172,2173,2175,2177,2179,2181],{"class":34,"line":174},[32,2174,71],{"class":70},[32,2176,75],{"class":74},[32,2178,79],{"class":78},[32,2180,82],{"class":70},[32,2182,85],{"class":78},[32,2184,2185,2187,2190,2192,2194,2196,2199,2202,2204,2206,2208,2210,2212,2215,2217,2220],{"class":34,"line":179},[32,2186,182],{"class":70},[32,2188,2189],{"class":70}," for",[32,2191,1019],{"class":78},[32,2193,847],{"class":70},[32,2195,1360],{"class":78},[32,2197,2198],{"class":70},"in",[32,2200,2201],{"class":74}," intervalCounter",[32,2203,94],{"class":78},[32,2205,195],{"class":112},[32,2207,559],{"class":78},[32,2209,562],{"class":70},[32,2211,573],{"class":112},[32,2213,2214],{"class":78},"), max",[32,2216,562],{"class":70},[32,2218,2219],{"class":112}," 3",[32,2221,2222],{"class":78},")) {\n",[32,2224,2225,2228,2231],{"class":34,"line":212},[32,2226,2227],{"class":74},"    print",[32,2229,2230],{"class":78},"(n);  ",[32,2232,2233],{"class":38},"\u002F\u002F 0 (after 1s), 1 (after 2s), 2 (after 3s)\n",[32,2235,2236],{"class":34,"line":217},[32,2237,773],{"class":78},[32,2239,2240,2242,2244,2247,2249],{"class":34,"line":223},[32,2241,91],{"class":74},[32,2243,94],{"class":78},[32,2245,2246],{"class":97},"'Done'",[32,2248,329],{"class":78},[32,2250,2251],{"class":38},"\u002F\u002F after 3s, when the stream closes\n",[32,2253,2254],{"class":34,"line":229},[32,2255,232],{"class":78},[32,2257,2258],{"class":34,"line":235},[32,2259,64],{"emptyLinePlaceholder":63},[32,2261,2262],{"class":34,"line":240},[32,2263,2264],{"class":38},"\u002F\u002F Consuming with listen (non-blocking, can cancel):\n",[32,2266,2267,2269,2272],{"class":34,"line":246},[32,2268,71],{"class":70},[32,2270,2271],{"class":74}," listenExample",[32,2273,1891],{"class":78},[32,2275,2276,2278,2281,2283,2285,2287,2289,2291,2293,2295,2298,2300],{"class":34,"line":252},[32,2277,996],{"class":70},[32,2279,2280],{"class":78}," sub ",[32,2282,315],{"class":70},[32,2284,2201],{"class":74},[32,2286,94],{"class":78},[32,2288,195],{"class":112},[32,2290,559],{"class":78},[32,2292,562],{"class":70},[32,2294,573],{"class":112},[32,2296,2297],{"class":78},")).",[32,2299,1551],{"class":74},[32,2301,690],{"class":78},[32,2303,2304,2307,2309,2311],{"class":34,"line":258},[32,2305,2306],{"class":78},"    (n) ",[32,2308,119],{"class":70},[32,2310,122],{"class":74},[32,2312,2313],{"class":78},"(n),\n",[32,2315,2316,2319,2321,2324,2326,2328,2330,2332,2334,2337,2339],{"class":34,"line":545},[32,2317,2318],{"class":78},"    onError",[32,2320,562],{"class":70},[32,2322,2323],{"class":78}," (e) ",[32,2325,119],{"class":70},[32,2327,122],{"class":74},[32,2329,94],{"class":78},[32,2331,1473],{"class":97},[32,2333,1476],{"class":97},[32,2335,2336],{"class":112},"e",[32,2338,1482],{"class":97},[32,2340,2341],{"class":78},"),\n",[32,2343,2344,2347,2349,2351,2353,2355,2357,2359],{"class":34,"line":581},[32,2345,2346],{"class":78},"    onDone",[32,2348,562],{"class":70},[32,2350,1953],{"class":78},[32,2352,119],{"class":70},[32,2354,122],{"class":74},[32,2356,94],{"class":78},[32,2358,2246],{"class":97},[32,2360,2341],{"class":78},[32,2362,2363],{"class":34,"line":611},[32,2364,2365],{"class":78},"  );\n",[32,2367,2368],{"class":34,"line":616},[32,2369,64],{"emptyLinePlaceholder":63},[32,2371,2372],{"class":34,"line":622},[32,2373,2374],{"class":38},"  \u002F\u002F Cancel after 5 seconds:\n",[32,2376,2377,2379,2381,2383,2385,2387,2389,2391,2394],{"class":34,"line":627},[32,2378,113],{"class":112},[32,2380,138],{"class":78},[32,2382,190],{"class":74},[32,2384,94],{"class":78},[32,2386,195],{"class":112},[32,2388,559],{"class":78},[32,2390,562],{"class":70},[32,2392,2393],{"class":112}," 5",[32,2395,2396],{"class":78},"), sub.cancel);\n",[32,2398,2399],{"class":34,"line":632},[32,2400,232],{"class":78},[32,2402,2403],{"class":34,"line":638},[32,2404,64],{"emptyLinePlaceholder":63},[32,2406,2407],{"class":34,"line":644},[32,2408,2409],{"class":38},"\u002F\u002F yield* delegates to another stream (flattens):\n",[32,2411,2412,2414,2416,2418,2420,2423,2425,2427],{"class":34,"line":650},[32,2413,1145],{"class":112},[32,2415,290],{"class":78},[32,2417,517],{"class":112},[32,2419,295],{"class":78},[32,2421,2422],{"class":74},"merged",[32,2424,79],{"class":78},[32,2426,2037],{"class":70},[32,2428,85],{"class":78},[32,2430,2431,2434,2436,2438,2440,2442,2444,2446,2448,2450,2452,2454],{"class":34,"line":670},[32,2432,2433],{"class":70},"  yield*",[32,2435,2201],{"class":74},[32,2437,94],{"class":78},[32,2439,195],{"class":112},[32,2441,559],{"class":78},[32,2443,562],{"class":70},[32,2445,573],{"class":112},[32,2447,2214],{"class":78},[32,2449,562],{"class":70},[32,2451,2219],{"class":112},[32,2453,329],{"class":78},[32,2455,2456],{"class":38},"\u002F\u002F 0, 1, 2\n",[32,2458,2459,2461,2463,2465,2467,2469,2471,2473,2475,2477,2479,2481],{"class":34,"line":678},[32,2460,2433],{"class":70},[32,2462,2201],{"class":74},[32,2464,94],{"class":78},[32,2466,195],{"class":112},[32,2468,559],{"class":78},[32,2470,562],{"class":70},[32,2472,565],{"class":112},[32,2474,2214],{"class":78},[32,2476,562],{"class":70},[32,2478,565],{"class":112},[32,2480,329],{"class":78},[32,2482,2483],{"class":38},"\u002F\u002F 0, 1 (slower)\n",[32,2485,2486],{"class":34,"line":693},[32,2487,232],{"class":78},[14,2489,2491],{"id":2490},"zones-error-isolation-context","Zones — Error Isolation & Context",[19,2493,2494],{"language":21},[23,2495,2497],{"className":25,"code":2496,"language":21,"meta":27,"style":27},"import 'dart:async';\n\n\u002F\u002F Zones provide an execution context that intercepts uncaught async errors,\n\u002F\u002F timers, and scheduleMicrotask calls. Use for top-level error boundaries.\n\nvoid main() {\n  \u002F\u002F runZonedGuarded catches ALL uncaught async errors in the zone.\n  runZonedGuarded(() async {\n    \u002F\u002F Any unawaited Future error here is caught by the zone handler.\n    Future.error('async error');  \u002F\u002F ← caught below, not crashed\n    throw 'sync error';           \u002F\u002F ← also caught\n  }, (error, stack) {\n    print('Uncaught: $error');\n    print('Stack: $stack');\n    \u002F\u002F Log to Sentry, Crashlytics, etc.\n  });\n\n  \u002F\u002F Without runZonedGuarded, unawaited Future errors crash the process\n  \u002F\u002F (or are silently swallowed in some configurations).\n}\n\n\u002F\u002F ❌ Anti-pattern: unawaited futures with errors — \"uncaught in the zone.\"\nvoid bad() {\n  Future.error('boom');  \u002F\u002F starts, no one awaits → uncaught async error\n  \u002F\u002F In Flutter: crashes the app (red screen in debug).\n  \u002F\u002F In CLI: may crash or be silently swallowed.\n}\n\n\u002F\u002F ✓ Correct: await, or explicitly mark as unawaited, and handle errors.\nvoid good() {\n  unawaited(\n    Future.error('boom').catchError((e) => print('Handled: $e')),\n  );\n}\n",[29,2498,2499,2507,2511,2516,2521,2525,2533,2538,2549,2554,2572,2586,2591,2608,2626,2631,2636,2640,2645,2650,2654,2658,2663,2672,2690,2695,2700,2704,2708,2713,2722,2729,2768,2772],{"__ignoreMap":27},[32,2500,2501,2503,2505],{"class":34,"line":35},[32,2502,832],{"class":70},[32,2504,1623],{"class":97},[32,2506,838],{"class":78},[32,2508,2509],{"class":34,"line":42},[32,2510,64],{"emptyLinePlaceholder":63},[32,2512,2513],{"class":34,"line":48},[32,2514,2515],{"class":38},"\u002F\u002F Zones provide an execution context that intercepts uncaught async errors,\n",[32,2517,2518],{"class":34,"line":54},[32,2519,2520],{"class":38},"\u002F\u002F timers, and scheduleMicrotask calls. Use for top-level error boundaries.\n",[32,2522,2523],{"class":34,"line":60},[32,2524,64],{"emptyLinePlaceholder":63},[32,2526,2527,2529,2531],{"class":34,"line":67},[32,2528,71],{"class":70},[32,2530,75],{"class":74},[32,2532,1891],{"class":78},[32,2534,2535],{"class":34,"line":88},[32,2536,2537],{"class":38},"  \u002F\u002F runZonedGuarded catches ALL uncaught async errors in the zone.\n",[32,2539,2540,2543,2545,2547],{"class":34,"line":104},[32,2541,2542],{"class":74},"  runZonedGuarded",[32,2544,116],{"class":78},[32,2546,82],{"class":70},[32,2548,85],{"class":78},[32,2550,2551],{"class":34,"line":109},[32,2552,2553],{"class":38},"    \u002F\u002F Any unawaited Future error here is caught by the zone handler.\n",[32,2555,2556,2558,2560,2562,2564,2567,2569],{"class":34,"line":133},[32,2557,548],{"class":112},[32,2559,138],{"class":78},[32,2561,1479],{"class":74},[32,2563,94],{"class":78},[32,2565,2566],{"class":97},"'async error'",[32,2568,329],{"class":78},[32,2570,2571],{"class":38},"\u002F\u002F ← caught below, not crashed\n",[32,2573,2574,2577,2580,2583],{"class":34,"line":157},[32,2575,2576],{"class":70},"    throw",[32,2578,2579],{"class":97}," 'sync error'",[32,2581,2582],{"class":78},";           ",[32,2584,2585],{"class":38},"\u002F\u002F ← also caught\n",[32,2587,2588],{"class":34,"line":162},[32,2589,2590],{"class":78},"  }, (error, stack) {\n",[32,2592,2593,2595,2597,2600,2602,2604,2606],{"class":34,"line":174},[32,2594,2227],{"class":74},[32,2596,94],{"class":78},[32,2598,2599],{"class":97},"'Uncaught: ",[32,2601,1476],{"class":97},[32,2603,1479],{"class":112},[32,2605,1482],{"class":97},[32,2607,101],{"class":78},[32,2609,2610,2612,2614,2617,2619,2622,2624],{"class":34,"line":179},[32,2611,2227],{"class":74},[32,2613,94],{"class":78},[32,2615,2616],{"class":97},"'Stack: ",[32,2618,1476],{"class":97},[32,2620,2621],{"class":112},"stack",[32,2623,1482],{"class":97},[32,2625,101],{"class":78},[32,2627,2628],{"class":34,"line":212},[32,2629,2630],{"class":38},"    \u002F\u002F Log to Sentry, Crashlytics, etc.\n",[32,2632,2633],{"class":34,"line":217},[32,2634,2635],{"class":78},"  });\n",[32,2637,2638],{"class":34,"line":223},[32,2639,64],{"emptyLinePlaceholder":63},[32,2641,2642],{"class":34,"line":229},[32,2643,2644],{"class":38},"  \u002F\u002F Without runZonedGuarded, unawaited Future errors crash the process\n",[32,2646,2647],{"class":34,"line":235},[32,2648,2649],{"class":38},"  \u002F\u002F (or are silently swallowed in some configurations).\n",[32,2651,2652],{"class":34,"line":240},[32,2653,232],{"class":78},[32,2655,2656],{"class":34,"line":246},[32,2657,64],{"emptyLinePlaceholder":63},[32,2659,2660],{"class":34,"line":252},[32,2661,2662],{"class":38},"\u002F\u002F ❌ Anti-pattern: unawaited futures with errors — \"uncaught in the zone.\"\n",[32,2664,2665,2667,2670],{"class":34,"line":258},[32,2666,71],{"class":70},[32,2668,2669],{"class":74}," bad",[32,2671,1891],{"class":78},[32,2673,2674,2676,2678,2680,2682,2685,2687],{"class":34,"line":545},[32,2675,113],{"class":112},[32,2677,138],{"class":78},[32,2679,1479],{"class":74},[32,2681,94],{"class":78},[32,2683,2684],{"class":97},"'boom'",[32,2686,329],{"class":78},[32,2688,2689],{"class":38},"\u002F\u002F starts, no one awaits → uncaught async error\n",[32,2691,2692],{"class":34,"line":581},[32,2693,2694],{"class":38},"  \u002F\u002F In Flutter: crashes the app (red screen in debug).\n",[32,2696,2697],{"class":34,"line":611},[32,2698,2699],{"class":38},"  \u002F\u002F In CLI: may crash or be silently swallowed.\n",[32,2701,2702],{"class":34,"line":616},[32,2703,232],{"class":78},[32,2705,2706],{"class":34,"line":622},[32,2707,64],{"emptyLinePlaceholder":63},[32,2709,2710],{"class":34,"line":627},[32,2711,2712],{"class":38},"\u002F\u002F ✓ Correct: await, or explicitly mark as unawaited, and handle errors.\n",[32,2714,2715,2717,2720],{"class":34,"line":632},[32,2716,71],{"class":70},[32,2718,2719],{"class":74}," good",[32,2721,1891],{"class":78},[32,2723,2724,2727],{"class":34,"line":638},[32,2725,2726],{"class":74},"  unawaited",[32,2728,690],{"class":78},[32,2730,2731,2733,2735,2737,2739,2741,2744,2747,2750,2752,2754,2756,2759,2761,2763,2765],{"class":34,"line":644},[32,2732,548],{"class":112},[32,2734,138],{"class":78},[32,2736,1479],{"class":74},[32,2738,94],{"class":78},[32,2740,2684],{"class":97},[32,2742,2743],{"class":78},").",[32,2745,2746],{"class":74},"catchError",[32,2748,2749],{"class":78},"((e) ",[32,2751,119],{"class":70},[32,2753,122],{"class":74},[32,2755,94],{"class":78},[32,2757,2758],{"class":97},"'Handled: ",[32,2760,1476],{"class":97},[32,2762,2336],{"class":112},[32,2764,1482],{"class":97},[32,2766,2767],{"class":78},")),\n",[32,2769,2770],{"class":34,"line":650},[32,2771,2365],{"class":78},[32,2773,2774],{"class":34,"line":670},[32,2775,232],{"class":78},[14,2777,2779],{"id":2778},"tips-tricks","💡 Tips & Tricks",[2781,2782,2783,2803,2821,2833,2841],"ul",{},[2784,2785,2786,2790,2791,2794,2795,2798,2799,2802],"li",{},[2787,2788,2789],"strong",{},"Idiom",": ",[29,2792,2793],{},"Future.wait"," for independent parallel operations — ",[29,2796,2797],{},"await Future.wait([fetchA(), fetchB(), fetchC()])"," runs all concurrently, total time = slowest. Result order matches input order, not completion order. Use ",[29,2800,2801],{},"eagerError: false"," to wait for all even on error.",[2784,2804,2805,2807,2808,2810,2811,2813,2814,2817,2818,2820],{},[2787,2806,2789],{},": use ",[29,2809,1247],{}," (not ",[29,2812,812],{},") for async stream transforms — ",[29,2815,2816],{},"stream.asyncMap((x) => fetch(x))"," awaits each Future, applying backpressure (the stream pauses until the Future completes). ",[29,2819,812],{}," is sync only.",[2784,2822,2823,2790,2825,2828,2829,2832],{},[2787,2824,2789],{},[29,2826,2827],{},"runZonedGuarded"," for top-level error boundaries — catches all uncaught async errors in the zone. Use in ",[29,2830,2831],{},"main"," to prevent unawaited Future errors from crashing silently. Log to error tracking (Sentry, Crashlytics).",[2784,2834,2835,2790,2837,2840],{},[2787,2836,2789],{},[29,2838,2839],{},"StreamController.broadcast()"," for multi-listener streams — event buses, shared state. Use single-subscription (default) for 1:1 pipelines with backpressure. Broadcast streams don't buffer for late listeners.",[2784,2842,2843,2846,2847,2850,2851,2854,2855,2857],{},[2787,2844,2845],{},"Performance",": bounded concurrency with ",[29,2848,2849],{},"package:pool"," for large-scale parallel operations — ",[29,2852,2853],{},"Pool(10).withResource(() => fetch(url))"," limits to 10 concurrent, preventing connection exhaustion. Don't ",[29,2856,2793],{}," thousands of items directly.",[14,2859,2861],{"id":2860},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2781,2863,2864,2879,2902,2922,2938,2953,2972,2987,3001,3021],{},[2784,2865,2866,2790,2871,2874,2875,2878],{},[2787,2867,2868,2870],{},[29,2869,800],{}," in a loop is sequential",[29,2872,2873],{},"for (var x in items) await fetch(x)"," runs one at a time. Use ",[29,2876,2877],{},"Future.wait(items.map(fetch))"," for parallel.",[2784,2880,2881,2790,2888,2891,2892,2895,2896,2898,2899,2901],{},[2787,2882,2883,2885,2886],{},[29,2884,82],{}," functions return ",[29,2887,287],{},[29,2889,2890],{},"int f() async { return 5; }"," returns ",[29,2893,2894],{},"Future\u003Cint>",", not ",[29,2897,517],{},". The ",[29,2900,82],{}," keyword wraps the return.",[2784,2903,2904,2790,2907,2910,2911,2913,2914,2917,2918,2921],{},[2787,2905,2906],{},"Unawaited Future errors are uncaught",[29,2908,2909],{},"Future.error('x');"," (no await) — the error is \"uncaught in the zone.\" Use ",[29,2912,2827],{}," or ",[29,2915,2916],{},".catchError",". Use ",[29,2919,2920],{},"unawaited(future)"," to suppress the lint if intentional.",[2784,2923,2924,2790,2927,2930,2931,2917,2934,2937],{},[2787,2925,2926],{},"Single-subscription streams allow one listener",[29,2928,2929],{},"stream.listen()"," twice throws ",[29,2932,2933],{},"StateError",[29,2935,2936],{},".asBroadcastStream()"," for multiple listeners — but broadcast streams can't be paused\u002Fbuffered.",[2784,2939,2940,2790,2946,2891,2949,2952],{},[2787,2941,2942,2945],{},[29,2943,2944],{},"Stream.toList()"," waits for close",[29,2947,2948],{},"stream.toList()",[29,2950,2951],{},"Future\u003CList>"," that completes when the stream closes. For an infinite stream, it never completes.",[2784,2954,2955,2790,2961,2964,2965,2968,2969,138],{},[2787,2956,2957,2960],{},[29,2958,2959],{},"await for"," blocks until stream closes",[29,2962,2963],{},"await for (var x in stream) { ... }"," doesn't exit until the stream closes (or ",[29,2966,2967],{},"break","). For infinite streams, use ",[29,2970,2971],{},".listen()",[2784,2973,2974,2790,2979,2982,2983,2986],{},[2787,2975,2976,2978],{},[29,2977,2793],{}," preserves input order",[29,2980,2981],{},"[slow, fast]"," → ",[29,2984,2985],{},"[slowResult, fastResult]"," in input order, NOT completion order.",[2784,2988,2989,2994,2995,2997,2998,3000],{},[2787,2990,2991,2993],{},[29,2992,2793],{}," short-circuits on error by default",": if one future fails, ",[29,2996,2793],{}," completes with that error immediately (other futures still run). Set ",[29,2999,2801],{}," to wait for all.",[2784,3002,3003,2790,3006,3009,3010,3013,3014,3016,3017,3020],{},[2787,3004,3005],{},"Microtask queue runs before event queue",[29,3007,3008],{},"Future.microtask()"," runs before ",[29,3011,3012],{},"Future()"," (which goes to the event queue). In Flutter, ",[29,3015,3012],{}," runs on the next frame; ",[29,3018,3019],{},"scheduleMicrotask"," runs before that.",[2784,3022,3023,3028,3029,3031,3032,3034,3035,138],{},[2787,3024,3025,3027],{},[29,3026,2037],{}," generators are single-subscription",": the stream from ",[29,3030,2037],{}," can only have one listener. For multiple listeners, use ",[29,3033,2936],{}," or a ",[29,3036,2839],{},[14,3038,3040],{"id":3039},"spot-the-bug","🧠 Spot the Bug",[3042,3043,3044,3045,3047],"p",{},"A developer processes a stream with ",[29,3046,1247],{}," that makes HTTP calls, but the stream seems to \"freeze\" after a few elements:",[19,3049,3050],{"language":21},[23,3051,3053],{"className":25,"code":3052,"language":21,"meta":27,"style":27},"Stream\u003Cint> ids = Stream.fromIterable([1, 2, 3, 4, 5]);\n\nStream\u003CData> dataStream = ids.asyncMap((id) async {\n  return await fetchFromApi(id);  \u002F\u002F each takes 2s\n});\n\nvoid main() async {\n  await for (var data in dataStream) {\n    print(data);\n  }\n}\n",[29,3054,3055,3097,3101,3127,3142,3147,3151,3163,3181,3188,3192],{"__ignoreMap":27},[32,3056,3057,3059,3061,3063,3066,3068,3070,3072,3074,3076,3078,3080,3082,3084,3086,3088,3090,3092,3095],{"class":34,"line":35},[32,3058,1145],{"class":112},[32,3060,290],{"class":78},[32,3062,517],{"class":112},[32,3064,3065],{"class":78},"> ids ",[32,3067,315],{"class":70},[32,3069,1397],{"class":112},[32,3071,138],{"class":78},[32,3073,1402],{"class":74},[32,3075,1405],{"class":78},[32,3077,1408],{"class":112},[32,3079,1411],{"class":78},[32,3081,1414],{"class":112},[32,3083,1411],{"class":78},[32,3085,1540],{"class":112},[32,3087,1411],{"class":78},[32,3089,1424],{"class":112},[32,3091,1411],{"class":78},[32,3093,3094],{"class":112},"5",[32,3096,1543],{"class":78},[32,3098,3099],{"class":34,"line":42},[32,3100,64],{"emptyLinePlaceholder":63},[32,3102,3103,3105,3107,3110,3113,3115,3118,3120,3123,3125],{"class":34,"line":48},[32,3104,1145],{"class":112},[32,3106,290],{"class":78},[32,3108,3109],{"class":112},"Data",[32,3111,3112],{"class":78},"> dataStream ",[32,3114,315],{"class":70},[32,3116,3117],{"class":78}," ids.",[32,3119,1247],{"class":74},[32,3121,3122],{"class":78},"((id) ",[32,3124,82],{"class":70},[32,3126,85],{"class":78},[32,3128,3129,3131,3133,3136,3139],{"class":34,"line":54},[32,3130,534],{"class":70},[32,3132,318],{"class":70},[32,3134,3135],{"class":74}," fetchFromApi",[32,3137,3138],{"class":78},"(id);  ",[32,3140,3141],{"class":38},"\u002F\u002F each takes 2s\n",[32,3143,3144],{"class":34,"line":60},[32,3145,3146],{"class":78},"});\n",[32,3148,3149],{"class":34,"line":67},[32,3150,64],{"emptyLinePlaceholder":63},[32,3152,3153,3155,3157,3159,3161],{"class":34,"line":88},[32,3154,71],{"class":70},[32,3156,75],{"class":74},[32,3158,79],{"class":78},[32,3160,82],{"class":70},[32,3162,85],{"class":78},[32,3164,3165,3167,3169,3171,3173,3176,3178],{"class":34,"line":104},[32,3166,182],{"class":70},[32,3168,2189],{"class":70},[32,3170,1019],{"class":78},[32,3172,1022],{"class":70},[32,3174,3175],{"class":78}," data ",[32,3177,2198],{"class":70},[32,3179,3180],{"class":78}," dataStream) {\n",[32,3182,3183,3185],{"class":34,"line":109},[32,3184,2227],{"class":74},[32,3186,3187],{"class":78},"(data);\n",[32,3189,3190],{"class":34,"line":133},[32,3191,773],{"class":78},[32,3193,3194],{"class":34,"line":157},[32,3195,232],{"class":78},[3042,3197,3198],{},"What's happening and is it a bug?",[3200,3201,3202,3206,3220,3226,3232,3563],"details",{},[3203,3204,3205],"summary",{},"Answer",[3042,3207,3208,3209,3212,3213,3216,3217,3219],{},"It's ",[2787,3210,3211],{},"not a bug"," — it's ",[2787,3214,3215],{},"backpressure",". ",[29,3218,1247],{}," awaits each Future before pulling the next element. The stream processes one element at a time: fetch(1) → 2s → emit → fetch(2) → 2s → emit → ... Total time: 10s for 5 elements.",[3042,3221,3222,3223,3225],{},"This is ",[29,3224,1247],{},"'s design: it applies backpressure, preventing the upstream from flooding a slow consumer. If the API call takes 2s, only one call is in-flight at a time.",[3042,3227,3228,3229,3231],{},"If you want ",[2787,3230,414],{}," processing (all fetches at once), you need a different approach:",[23,3233,3235],{"className":25,"code":3234,"language":21,"meta":27,"style":27},"\u002F\u002F Option 1: collect all, then Future.wait (loses streaming):\nFuture\u003CList\u003CData>> fetchAll(Stream\u003Cint> ids) async {\n  final idList = await ids.toList();\n  return Future.wait(idList.map(fetchFromApi));\n}\n\n\u002F\u002F Option 2: use a bounded pool with a broadcast controller:\nStream\u003CData> fetchParallel(Stream\u003Cint> ids, {int concurrency = 3}) {\n  final controller = StreamController\u003CData>();\n  final pool = Pool(concurrency);\n  var pending = 0;\n  var done = false;\n\n  ids.listen((id) {\n    pending++;\n    pool.withResource(() => fetchFromApi(id)).then((data) {\n      controller.add(data);\n      pending--;\n      if (done && pending == 0) controller.close();\n    });\n  }, onDone: () {\n    done = true;\n    if (pending == 0) controller.close();\n  });\n\n  return controller.stream;\n}\n",[29,3236,3237,3242,3273,3291,3309,3313,3317,3322,3358,3374,3387,3400,3413,3417,3427,3436,3458,3467,3477,3501,3505,3515,3526,3544,3548,3552,3559],{"__ignoreMap":27},[32,3238,3239],{"class":34,"line":35},[32,3240,3241],{"class":38},"\u002F\u002F Option 1: collect all, then Future.wait (loses streaming):\n",[32,3243,3244,3246,3248,3250,3252,3254,3256,3258,3260,3262,3264,3266,3269,3271],{"class":34,"line":42},[32,3245,287],{"class":112},[32,3247,290],{"class":78},[32,3249,512],{"class":112},[32,3251,290],{"class":78},[32,3253,3109],{"class":112},[32,3255,520],{"class":78},[32,3257,883],{"class":74},[32,3259,94],{"class":78},[32,3261,1145],{"class":112},[32,3263,290],{"class":78},[32,3265,517],{"class":112},[32,3267,3268],{"class":78},"> ids) ",[32,3270,82],{"class":70},[32,3272,85],{"class":78},[32,3274,3275,3277,3280,3282,3284,3286,3289],{"class":34,"line":48},[32,3276,996],{"class":70},[32,3278,3279],{"class":78}," idList ",[32,3281,315],{"class":70},[32,3283,318],{"class":70},[32,3285,3117],{"class":78},[32,3287,3288],{"class":74},"toList",[32,3290,1584],{"class":78},[32,3292,3293,3295,3297,3299,3301,3304,3306],{"class":34,"line":54},[32,3294,534],{"class":70},[32,3296,185],{"class":112},[32,3298,138],{"class":78},[32,3300,438],{"class":74},[32,3302,3303],{"class":78},"(idList.",[32,3305,812],{"class":74},[32,3307,3308],{"class":78},"(fetchFromApi));\n",[32,3310,3311],{"class":34,"line":60},[32,3312,232],{"class":78},[32,3314,3315],{"class":34,"line":67},[32,3316,64],{"emptyLinePlaceholder":63},[32,3318,3319],{"class":34,"line":88},[32,3320,3321],{"class":38},"\u002F\u002F Option 2: use a bounded pool with a broadcast controller:\n",[32,3323,3324,3326,3328,3330,3332,3335,3337,3339,3341,3343,3346,3348,3351,3353,3355],{"class":34,"line":104},[32,3325,1145],{"class":112},[32,3327,290],{"class":78},[32,3329,3109],{"class":112},[32,3331,295],{"class":78},[32,3333,3334],{"class":74},"fetchParallel",[32,3336,94],{"class":78},[32,3338,1145],{"class":112},[32,3340,290],{"class":78},[32,3342,517],{"class":112},[32,3344,3345],{"class":78},"> ids, {",[32,3347,517],{"class":112},[32,3349,3350],{"class":78}," concurrency ",[32,3352,315],{"class":70},[32,3354,2219],{"class":112},[32,3356,3357],{"class":78},"}) {\n",[32,3359,3360,3362,3364,3366,3368,3370,3372],{"class":34,"line":109},[32,3361,996],{"class":70},[32,3363,1932],{"class":78},[32,3365,315],{"class":70},[32,3367,1665],{"class":112},[32,3369,290],{"class":78},[32,3371,3109],{"class":112},[32,3373,1831],{"class":78},[32,3375,3376,3378,3380,3382,3384],{"class":34,"line":133},[32,3377,996],{"class":70},[32,3379,850],{"class":78},[32,3381,315],{"class":70},[32,3383,855],{"class":112},[32,3385,3386],{"class":78},"(concurrency);\n",[32,3388,3389,3391,3394,3396,3398],{"class":34,"line":157},[32,3390,309],{"class":70},[32,3392,3393],{"class":78}," pending ",[32,3395,315],{"class":70},[32,3397,1030],{"class":112},[32,3399,838],{"class":78},[32,3401,3402,3404,3407,3409,3411],{"class":34,"line":162},[32,3403,309],{"class":70},[32,3405,3406],{"class":78}," done ",[32,3408,315],{"class":70},[32,3410,737],{"class":112},[32,3412,838],{"class":78},[32,3414,3415],{"class":34,"line":174},[32,3416,64],{"emptyLinePlaceholder":63},[32,3418,3419,3422,3424],{"class":34,"line":179},[32,3420,3421],{"class":78},"  ids.",[32,3423,1551],{"class":74},[32,3425,3426],{"class":78},"((id) {\n",[32,3428,3429,3432,3434],{"class":34,"line":212},[32,3430,3431],{"class":78},"    pending",[32,3433,2151],{"class":70},[32,3435,838],{"class":78},[32,3437,3438,3441,3443,3445,3447,3449,3452,3455],{"class":34,"line":217},[32,3439,3440],{"class":78},"    pool.",[32,3442,919],{"class":74},[32,3444,116],{"class":78},[32,3446,119],{"class":70},[32,3448,3135],{"class":74},[32,3450,3451],{"class":78},"(id)).",[32,3453,3454],{"class":74},"then",[32,3456,3457],{"class":78},"((data) {\n",[32,3459,3460,3463,3465],{"class":34,"line":223},[32,3461,3462],{"class":78},"      controller.",[32,3464,1731],{"class":74},[32,3466,3187],{"class":78},[32,3468,3469,3472,3475],{"class":34,"line":229},[32,3470,3471],{"class":78},"      pending",[32,3473,3474],{"class":70},"--",[32,3476,838],{"class":78},[32,3478,3479,3482,3485,3488,3490,3492,3494,3497,3499],{"class":34,"line":235},[32,3480,3481],{"class":70},"      if",[32,3483,3484],{"class":78}," (done ",[32,3486,3487],{"class":70},"&&",[32,3489,3393],{"class":78},[32,3491,2114],{"class":70},[32,3493,1030],{"class":112},[32,3495,3496],{"class":78},") controller.",[32,3498,1775],{"class":74},[32,3500,1584],{"class":78},[32,3502,3503],{"class":34,"line":240},[32,3504,1372],{"class":78},[32,3506,3507,3510,3512],{"class":34,"line":246},[32,3508,3509],{"class":78},"  }, onDone",[32,3511,562],{"class":70},[32,3513,3514],{"class":78}," () {\n",[32,3516,3517,3520,3522,3524],{"class":34,"line":252},[32,3518,3519],{"class":78},"    done ",[32,3521,315],{"class":70},[32,3523,2022],{"class":112},[32,3525,838],{"class":78},[32,3527,3528,3531,3534,3536,3538,3540,3542],{"class":34,"line":258},[32,3529,3530],{"class":70},"    if",[32,3532,3533],{"class":78}," (pending ",[32,3535,2114],{"class":70},[32,3537,1030],{"class":112},[32,3539,3496],{"class":78},[32,3541,1775],{"class":74},[32,3543,1584],{"class":78},[32,3545,3546],{"class":34,"line":545},[32,3547,2635],{"class":78},[32,3549,3550],{"class":34,"line":581},[32,3551,64],{"emptyLinePlaceholder":63},[32,3553,3554,3556],{"class":34,"line":611},[32,3555,534],{"class":70},[32,3557,3558],{"class":78}," controller.stream;\n",[32,3560,3561],{"class":34,"line":616},[32,3562,232],{"class":78},[3042,3564,3565,3566,3568],{},"The \"freeze\" is actually correct behavior — ",[29,3567,1247],{}," serializes operations. For parallel streaming, use a pool + controller pattern. Know which semantics you need.",[3570,3571,3572],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":27,"searchDepth":42,"depth":42,"links":3574},[3575,3576,3579,3580,3581,3583,3584,3585,3586],{"id":16,"depth":42,"text":17},{"id":264,"depth":42,"text":265,"children":3577},[3578],{"id":782,"depth":48,"text":783},{"id":1113,"depth":42,"text":1114},{"id":1608,"depth":42,"text":1609},{"id":2034,"depth":42,"text":3582},"async* Generators — Lazy Stream Production",{"id":2490,"depth":42,"text":2491},{"id":2778,"depth":42,"text":2779},{"id":2860,"depth":42,"text":2861},{"id":3039,"depth":42,"text":3040},"Deep-dive into Dart's event loop, Future composition patterns, Stream transformation pipelines, async generator semantics, Zone-based error isolation, and concurrency pitfalls. Code-first engineering reference.","md",{},"\u002Fdart\u002F09-async-programming",{"title":5,"description":3587},"dart\u002F09-async-programming","iPm9e4ExFVUnYWtjENgW0a758nuneGCCckA0WDVpnA0",1789924647735]