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