[{"data":1,"prerenderedAt":1338},["ShallowReactive",2],{"navigation":3,"\u002Farticles\u002Fcallback":54,"\u002Farticles\u002Fcallback-surround":1333},[4],{"title":5,"path":6,"stem":7,"children":8,"page":53},"Articles","\u002Farticles","articles",[9,13,17,21,25,29,33,37,41,45,49],{"title":10,"path":11,"stem":12},"Async 到底在解決什麼問題？","\u002Farticles\u002Fasync-waiting","articles\u002Fasync-waiting",{"title":14,"path":15,"stem":16},"Callback 是什麼？","\u002Farticles\u002Fcallback","articles\u002Fcallback",{"title":18,"path":19,"stem":20},"Claude Code 的 Skill、MCP、Hook、Plugin 分別是什麼？","\u002Farticles\u002Fclaude-code-skill-mcp-hook-plugin","articles\u002Fclaude-code-skill-mcp-hook-plugin",{"title":22,"path":23,"stem":24},"用 Daily Snapshot 提升統計查詢速度","\u002Farticles\u002Fdaily-snapshot","articles\u002Fdaily-snapshot",{"title":26,"path":27,"stem":28},"GKE 部署","\u002Farticles\u002Fgke-deployment","articles\u002Fgke-deployment",{"title":30,"path":31,"stem":32},"I\u002FO 密集 vs CPU 密集：執行緒數該開多少？","\u002Farticles\u002Fio-bound-vs-cpu-bound","articles\u002Fio-bound-vs-cpu-bound",{"title":34,"path":35,"stem":36},"從 LLM 到 Agent：打通底層邏輯","\u002Farticles\u002Fllm-to-agent","articles\u002Fllm-to-agent",{"title":38,"path":39,"stem":40},"資訊安全實踐","\u002Farticles\u002Fsecurity-best-practices","articles\u002Fsecurity-best-practices",{"title":42,"path":43,"stem":44},"單機架構的性能優化","\u002Farticles\u002Fsingle-machine-performance","articles\u002Fsingle-machine-performance",{"title":46,"path":47,"stem":48},"伺服器渲染 SSR","\u002Farticles\u002Fssr","articles\u002Fssr",{"title":50,"path":51,"stem":52},"同步 vs 非同步","\u002Farticles\u002Fsync-vs-async","articles\u002Fsync-vs-async",false,{"id":55,"title":14,"author":56,"body":60,"date":1325,"description":1326,"extension":1327,"externalUrl":1328,"image":1329,"meta":1330,"minRead":195,"navigation":232,"path":15,"seo":1331,"stem":16,"__hash__":1332},"blog\u002Farticles\u002Fcallback.md",{"name":57,"avatar":58},"Gary",{"src":59,"alt":57},"\u002Fimages\u002Fselfie.webp",{"type":61,"value":62,"toc":1315},"minimark",[63,67,80,83,87,93,96,114,306,308,311,325,331,333,337,344,349,408,413,528,531,533,537,540,614,616,620,625,838,843,979,1088,1095,1097,1101,1108,1251,1254,1278,1280,1284,1297,1303,1311],[64,65,66],"h2",{"id":66},"一句話定義",[68,69,70,77],"blockquote",{},[71,72,73],"p",{},[74,75,76],"strong",{},"被交給另一段程式碼、由對方決定何時呼叫的函式。",[71,78,79],{},"A function handed to other code, to be called back by it — not by whoever defined it.",[81,82],"hr",{},[64,84,86],{"id":85},"callback-的判斷標準","Callback 的判斷標準",[71,88,89,92],{},[74,90,91],{},"Callback（回呼函式）","：把一個函式交給另一段程式碼，由「接收方」決定何時呼叫它。",[71,94,95],{},"要成立，需同時符合兩個條件：",[97,98,99,107],"ol",{},[100,101,102,103,106],"li",{},"這個函式是被",[74,104,105],{},"交出去","的（不是寫死在裡面直接呼叫）",[100,108,109,110,113],{},"由",[74,111,112],{},"接收方","決定何時、是否呼叫它——定義這個函式的人不會自己主動呼叫",[115,116,121],"pre",{"className":117,"code":118,"language":119,"meta":120,"style":120},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ✅ 是 callback：b 被交給 a，由 a 決定何時呼叫\nfunction a(callback) {\n  callback()\n}\na(function b() {\n  console.log('b')\n})\n\n\u002F\u002F ❌ 不是 callback：a 內部寫死直接呼叫 b，不是被交出去的\nfunction b() {\n  console.log('b')\n}\nfunction a() {\n  b()\n}\na()\n","js","",[122,123,124,133,158,168,174,193,219,227,234,240,251,270,275,286,294,299],"code",{"__ignoreMap":120},[125,126,129],"span",{"class":127,"line":128},"line",1,[125,130,132],{"class":131},"sHwdD","\u002F\u002F ✅ 是 callback：b 被交給 a，由 a 決定何時呼叫\n",[125,134,136,140,144,148,152,155],{"class":127,"line":135},2,[125,137,139],{"class":138},"spNyl","function",[125,141,143],{"class":142},"s2Zo4"," a",[125,145,147],{"class":146},"sMK4o","(",[125,149,151],{"class":150},"sHdIc","callback",[125,153,154],{"class":146},")",[125,156,157],{"class":146}," {\n",[125,159,161,164],{"class":127,"line":160},3,[125,162,163],{"class":142},"  callback",[125,165,167],{"class":166},"swJcz","()\n",[125,169,171],{"class":127,"line":170},4,[125,172,173],{"class":146},"}\n",[125,175,177,180,183,185,188,191],{"class":127,"line":176},5,[125,178,179],{"class":142},"a",[125,181,147],{"class":182},"sTEyZ",[125,184,139],{"class":138},[125,186,187],{"class":142}," b",[125,189,190],{"class":146},"()",[125,192,157],{"class":146},[125,194,196,199,202,205,207,210,214,216],{"class":127,"line":195},6,[125,197,198],{"class":182},"  console",[125,200,201],{"class":146},".",[125,203,204],{"class":142},"log",[125,206,147],{"class":166},[125,208,209],{"class":146},"'",[125,211,213],{"class":212},"sfazB","b",[125,215,209],{"class":146},[125,217,218],{"class":166},")\n",[125,220,222,225],{"class":127,"line":221},7,[125,223,224],{"class":146},"}",[125,226,218],{"class":182},[125,228,230],{"class":127,"line":229},8,[125,231,233],{"emptyLinePlaceholder":232},true,"\n",[125,235,237],{"class":127,"line":236},9,[125,238,239],{"class":131},"\u002F\u002F ❌ 不是 callback：a 內部寫死直接呼叫 b，不是被交出去的\n",[125,241,243,245,247,249],{"class":127,"line":242},10,[125,244,139],{"class":138},[125,246,187],{"class":142},[125,248,190],{"class":146},[125,250,157],{"class":146},[125,252,254,256,258,260,262,264,266,268],{"class":127,"line":253},11,[125,255,198],{"class":182},[125,257,201],{"class":146},[125,259,204],{"class":142},[125,261,147],{"class":166},[125,263,209],{"class":146},[125,265,213],{"class":212},[125,267,209],{"class":146},[125,269,218],{"class":166},[125,271,273],{"class":127,"line":272},12,[125,274,173],{"class":146},[125,276,278,280,282,284],{"class":127,"line":277},13,[125,279,139],{"class":138},[125,281,143],{"class":142},[125,283,190],{"class":146},[125,285,157],{"class":146},[125,287,289,292],{"class":127,"line":288},14,[125,290,291],{"class":142},"  b",[125,293,167],{"class":166},[125,295,297],{"class":127,"line":296},15,[125,298,173],{"class":146},[125,300,302,304],{"class":127,"line":301},16,[125,303,179],{"class":142},[125,305,167],{"class":182},[81,307],{},[64,309,310],{"id":310},"常見誤解澄清",[71,312,313,316,317,320,321,324],{},[74,314,315],{},"誤解一：callback 只跟非同步有關","\n不對。",[122,318,319],{},"forEach"," 裡的 callback 是同步執行；",[122,322,323],{},"setTimeout"," 裡的 callback 是非同步執行，兩者都叫 callback。callback 描述的是「誰決定何時呼叫」，跟執行時機是同步還是非同步無關。",[71,326,327,330],{},[74,328,329],{},"誤解二：callback 是 JavaScript 專屬的東西","\n不對，這是跨語言通用的設計模式。只要語言支援把函式當一級公民傳遞（JS、Python、Go），或至少支援函式指標／介面模擬（C、舊版 Java），都能實現 callback。",[81,332],{},[64,334,336],{"id":335},"不只是當參數傳","不只是「當參數傳」",[71,338,339,340,343],{},"「當參數傳」只是最常見的形式，不是唯一形式。真正的本質是：",[74,341,342],{},"函式的定義者不會自己呼叫它，而是交出去，讓另一方保管、決定何時觸發","。除了參數傳遞，還有其他一樣算 callback 的形式：",[71,345,346],{},[74,347,348],{},"賦值給屬性",[115,350,352],{"className":117,"code":351,"language":119,"meta":120,"style":120},"window.onload = function () {\n  console.log('頁面載入完成')\n}\n\u002F\u002F 瀏覽器內部某個時機點會主動呼叫 window.onload()\n\u002F\u002F 沒有透過參數，但一樣是 callback——由瀏覽器決定何時呼叫\n",[122,353,354,375,394,398,403],{"__ignoreMap":120},[125,355,356,359,361,364,367,370,373],{"class":127,"line":128},[125,357,358],{"class":182},"window",[125,360,201],{"class":146},[125,362,363],{"class":142},"onload",[125,365,366],{"class":146}," =",[125,368,369],{"class":138}," function",[125,371,372],{"class":146}," ()",[125,374,157],{"class":146},[125,376,377,379,381,383,385,387,390,392],{"class":127,"line":135},[125,378,198],{"class":182},[125,380,201],{"class":146},[125,382,204],{"class":142},[125,384,147],{"class":166},[125,386,209],{"class":146},[125,388,389],{"class":212},"頁面載入完成",[125,391,209],{"class":146},[125,393,218],{"class":166},[125,395,396],{"class":127,"line":160},[125,397,173],{"class":146},[125,399,400],{"class":127,"line":170},[125,401,402],{"class":131},"\u002F\u002F 瀏覽器內部某個時機點會主動呼叫 window.onload()\n",[125,404,405],{"class":127,"line":176},[125,406,407],{"class":131},"\u002F\u002F 沒有透過參數，但一樣是 callback——由瀏覽器決定何時呼叫\n",[71,409,410],{},[74,411,412],{},"註冊到集合／清單裡",[115,414,416],{"className":117,"code":415,"language":119,"meta":120,"style":120},"class EventBus {\n  handlers = []\n  on(fn) {\n    this.handlers.push(fn) \u002F\u002F 存進陣列，不是當下被呼叫\n  }\n  emit() {\n    this.handlers.forEach((fn) => fn()) \u002F\u002F 之後才被呼叫\n  }\n}\n",[122,417,418,429,439,453,476,481,490,520,524],{"__ignoreMap":120},[125,419,420,423,427],{"class":127,"line":128},[125,421,422],{"class":138},"class",[125,424,426],{"class":425},"sBMFI"," EventBus",[125,428,157],{"class":146},[125,430,431,434,436],{"class":127,"line":135},[125,432,433],{"class":166},"  handlers",[125,435,366],{"class":146},[125,437,438],{"class":182}," []\n",[125,440,441,444,446,449,451],{"class":127,"line":160},[125,442,443],{"class":166},"  on",[125,445,147],{"class":146},[125,447,448],{"class":150},"fn",[125,450,154],{"class":146},[125,452,157],{"class":146},[125,454,455,458,461,463,466,468,470,473],{"class":127,"line":170},[125,456,457],{"class":146},"    this.",[125,459,460],{"class":182},"handlers",[125,462,201],{"class":146},[125,464,465],{"class":142},"push",[125,467,147],{"class":166},[125,469,448],{"class":182},[125,471,472],{"class":166},") ",[125,474,475],{"class":131},"\u002F\u002F 存進陣列，不是當下被呼叫\n",[125,477,478],{"class":127,"line":176},[125,479,480],{"class":146},"  }\n",[125,482,483,486,488],{"class":127,"line":195},[125,484,485],{"class":166},"  emit",[125,487,190],{"class":146},[125,489,157],{"class":146},[125,491,492,494,496,498,500,502,504,506,508,511,514,517],{"class":127,"line":221},[125,493,457],{"class":146},[125,495,460],{"class":182},[125,497,201],{"class":146},[125,499,319],{"class":142},[125,501,147],{"class":166},[125,503,147],{"class":146},[125,505,448],{"class":150},[125,507,154],{"class":146},[125,509,510],{"class":138}," =>",[125,512,513],{"class":142}," fn",[125,515,516],{"class":166},"()) ",[125,518,519],{"class":131},"\u002F\u002F 之後才被呼叫\n",[125,521,522],{"class":127,"line":229},[125,523,480],{"class":146},[125,525,526],{"class":127,"line":236},[125,527,173],{"class":146},[71,529,530],{},"參數傳遞之所以最常見，是因為在支援一級函式的語言裡，它不需要額外宣告變數去存放，語意也最直接：「這個函式，麻煩你決定何時呼叫」，跟函式呼叫的語法自然結合，讀起來一目了然。屬性賦值、註冊清單本質上做的是同一件事，只是把函式參照存放到別的地方。",[81,532],{},[64,534,536],{"id":535},"由誰決定何時呼叫","由誰決定何時呼叫？",[71,538,539],{},"接收方不一定是個「函數」，可以是外層函式、runtime、事件系統、甚至 OS。",[541,542,543,556],"table",{},[544,545,546],"thead",{},[547,548,549,553],"tr",{},[550,551,552],"th",{},"範例",[550,554,555],{},"誰決定何時呼叫",[557,558,559,572,582,592,602],"tbody",{},[547,560,561,567],{},[562,563,564],"td",{},[122,565,566],{},"a(callback)",[562,568,569,570],{},"外層函式 ",[122,571,179],{},[547,573,574,579],{},[562,575,576],{},[122,577,578],{},"window.onload = fn",[562,580,581],{},"瀏覽器（runtime），等頁面載入完才呼叫",[547,583,584,589],{},[562,585,586],{},[122,587,588],{},"setTimeout(fn, 1000)",[562,590,591],{},"瀏覽器的 Timer 機制，等時間到才呼叫",[547,593,594,599],{},[562,595,596],{},[122,597,598],{},"element.onclick = fn",[562,600,601],{},"瀏覽器的事件系統，等使用者點擊才呼叫",[547,603,604,609],{},[562,605,606],{},[122,607,608],{},"array.forEach(fn)",[562,610,611,613],{},[122,612,319],{}," 內部邏輯，依序對每個元素呼叫",[81,615],{},[64,617,619],{"id":618},"js-中常見使用-callback-的函數","JS 中常見使用 callback 的函數",[71,621,622],{},[74,623,624],{},"同步（Sync）——陣列、字串操作",[115,626,628],{"className":117,"code":627,"language":119,"meta":120,"style":120},"array.forEach((item) => {})\narray.map((item) => item * 2)\narray.filter((item) => item > 5)\narray.reduce((acc, item) => acc + item, 0)\narray.sort((a, b) => a - b)\n'hello'.replace(\u002Fl\u002Fg, (m) => m.toUpperCase())\n",[122,629,630,655,686,715,756,788],{"__ignoreMap":120},[125,631,632,635,637,639,641,643,646,648,650,653],{"class":127,"line":128},[125,633,634],{"class":182},"array",[125,636,201],{"class":146},[125,638,319],{"class":142},[125,640,147],{"class":182},[125,642,147],{"class":146},[125,644,645],{"class":150},"item",[125,647,154],{"class":146},[125,649,510],{"class":138},[125,651,652],{"class":146}," {}",[125,654,218],{"class":182},[125,656,657,659,661,664,666,668,670,672,674,677,680,684],{"class":127,"line":135},[125,658,634],{"class":182},[125,660,201],{"class":146},[125,662,663],{"class":142},"map",[125,665,147],{"class":182},[125,667,147],{"class":146},[125,669,645],{"class":150},[125,671,154],{"class":146},[125,673,510],{"class":138},[125,675,676],{"class":182}," item ",[125,678,679],{"class":146},"*",[125,681,683],{"class":682},"sbssI"," 2",[125,685,218],{"class":182},[125,687,688,690,692,695,697,699,701,703,705,707,710,713],{"class":127,"line":160},[125,689,634],{"class":182},[125,691,201],{"class":146},[125,693,694],{"class":142},"filter",[125,696,147],{"class":182},[125,698,147],{"class":146},[125,700,645],{"class":150},[125,702,154],{"class":146},[125,704,510],{"class":138},[125,706,676],{"class":182},[125,708,709],{"class":146},">",[125,711,712],{"class":682}," 5",[125,714,218],{"class":182},[125,716,717,719,721,724,726,728,731,734,737,739,741,744,747,749,751,754],{"class":127,"line":170},[125,718,634],{"class":182},[125,720,201],{"class":146},[125,722,723],{"class":142},"reduce",[125,725,147],{"class":182},[125,727,147],{"class":146},[125,729,730],{"class":150},"acc",[125,732,733],{"class":146},",",[125,735,736],{"class":150}," item",[125,738,154],{"class":146},[125,740,510],{"class":138},[125,742,743],{"class":182}," acc ",[125,745,746],{"class":146},"+",[125,748,736],{"class":182},[125,750,733],{"class":146},[125,752,753],{"class":682}," 0",[125,755,218],{"class":182},[125,757,758,760,762,765,767,769,771,773,775,777,779,782,785],{"class":127,"line":176},[125,759,634],{"class":182},[125,761,201],{"class":146},[125,763,764],{"class":142},"sort",[125,766,147],{"class":182},[125,768,147],{"class":146},[125,770,179],{"class":150},[125,772,733],{"class":146},[125,774,187],{"class":150},[125,776,154],{"class":146},[125,778,510],{"class":138},[125,780,781],{"class":182}," a ",[125,783,784],{"class":146},"-",[125,786,787],{"class":182}," b)\n",[125,789,790,792,795,797,799,802,804,807,810,812,815,817,820,823,825,827,830,832,835],{"class":127,"line":195},[125,791,209],{"class":146},[125,793,794],{"class":212},"hello",[125,796,209],{"class":146},[125,798,201],{"class":146},[125,800,801],{"class":142},"replace",[125,803,147],{"class":182},[125,805,806],{"class":146},"\u002F",[125,808,809],{"class":212},"l",[125,811,806],{"class":146},[125,813,814],{"class":682},"g",[125,816,733],{"class":146},[125,818,819],{"class":146}," (",[125,821,822],{"class":150},"m",[125,824,154],{"class":146},[125,826,510],{"class":138},[125,828,829],{"class":182}," m",[125,831,201],{"class":146},[125,833,834],{"class":142},"toUpperCase",[125,836,837],{"class":182},"())\n",[71,839,840],{},[74,841,842],{},"非同步（Async）——計時器、事件、I\u002FO",[115,844,846],{"className":117,"code":845,"language":119,"meta":120,"style":120},"setTimeout(() => {}, 1000)\nelement.addEventListener('click', (e) => {})\nfs.readFile('file.txt', (err, data) => {}) \u002F\u002F Node.js\nemitter.on('data', (data) => {})           \u002F\u002F EventEmitter\n",[122,847,848,866,900,942],{"__ignoreMap":120},[125,849,850,852,854,856,858,861,864],{"class":127,"line":128},[125,851,323],{"class":142},[125,853,147],{"class":182},[125,855,190],{"class":146},[125,857,510],{"class":138},[125,859,860],{"class":146}," {},",[125,862,863],{"class":682}," 1000",[125,865,218],{"class":182},[125,867,868,871,873,876,878,880,883,885,887,889,892,894,896,898],{"class":127,"line":135},[125,869,870],{"class":182},"element",[125,872,201],{"class":146},[125,874,875],{"class":142},"addEventListener",[125,877,147],{"class":182},[125,879,209],{"class":146},[125,881,882],{"class":212},"click",[125,884,209],{"class":146},[125,886,733],{"class":146},[125,888,819],{"class":146},[125,890,891],{"class":150},"e",[125,893,154],{"class":146},[125,895,510],{"class":138},[125,897,652],{"class":146},[125,899,218],{"class":182},[125,901,902,905,907,910,912,914,917,919,921,923,926,928,931,933,935,937,939],{"class":127,"line":160},[125,903,904],{"class":182},"fs",[125,906,201],{"class":146},[125,908,909],{"class":142},"readFile",[125,911,147],{"class":182},[125,913,209],{"class":146},[125,915,916],{"class":212},"file.txt",[125,918,209],{"class":146},[125,920,733],{"class":146},[125,922,819],{"class":146},[125,924,925],{"class":150},"err",[125,927,733],{"class":146},[125,929,930],{"class":150}," data",[125,932,154],{"class":146},[125,934,510],{"class":138},[125,936,652],{"class":146},[125,938,472],{"class":182},[125,940,941],{"class":131},"\u002F\u002F Node.js\n",[125,943,944,947,949,952,954,956,959,961,963,965,967,969,971,973,976],{"class":127,"line":170},[125,945,946],{"class":182},"emitter",[125,948,201],{"class":146},[125,950,951],{"class":142},"on",[125,953,147],{"class":182},[125,955,209],{"class":146},[125,957,958],{"class":212},"data",[125,960,209],{"class":146},[125,962,733],{"class":146},[125,964,819],{"class":146},[125,966,958],{"class":150},[125,968,154],{"class":146},[125,970,510],{"class":138},[125,972,652],{"class":146},[125,974,975],{"class":182},")           ",[125,977,978],{"class":131},"\u002F\u002F EventEmitter\n",[541,980,981,994],{},[544,982,983],{},[547,984,985,988,991],{},[550,986,987],{},"類別",[550,989,990],{},"常見函數",[550,992,993],{},"同步\u002F非同步",[557,995,996,1017,1032,1049,1064],{},[547,997,998,1001,1014],{},[562,999,1000],{},"陣列操作",[562,1002,1003,1005,1006,1005,1008,1005,1010,1005,1012],{},[122,1004,319],{},"、",[122,1007,663],{},[122,1009,694],{},[122,1011,723],{},[122,1013,764],{},[562,1015,1016],{},"同步",[547,1018,1019,1022,1029],{},[562,1020,1021],{},"計時器",[562,1023,1024,1005,1026],{},[122,1025,323],{},[122,1027,1028],{},"setInterval",[562,1030,1031],{},"非同步",[547,1033,1034,1037,1047],{},[562,1035,1036],{},"事件監聽",[562,1038,1039,1005,1041,1005,1044],{},[122,1040,875],{},[122,1042,1043],{},"onclick",[122,1045,1046],{},"EventEmitter.on",[562,1048,1031],{},[547,1050,1051,1054,1062],{},[562,1052,1053],{},"檔案／系統 I\u002FO（Node）",[562,1055,1056,1005,1059],{},[122,1057,1058],{},"fs.readFile",[122,1060,1061],{},"child_process.exec",[562,1063,1031],{},[547,1065,1066,1069,1078],{},[562,1067,1068],{},"Promise",[562,1070,1071,1005,1074,1077],{},[122,1072,1073],{},".then",[122,1075,1076],{},".catch","、executor",[562,1079,1080,1081,806,1084,1087],{},"混合（executor 同步，",[122,1082,1083],{},"then",[122,1085,1086],{},"catch"," 非同步）",[71,1089,1090,1091,1094],{},"就「用量」而言，同步 callback（陣列方法）用得最頻繁，只是太日常不會被特別提起。但「callback」這個詞會變成廣泛討論、甚至帶點負面印象的技術名詞（callback hell），主要來自",[74,1092,1093],{},"非同步場景","——因為在那裡，callback 是解決「無法立刻拿到結果」問題的必要手段，不像同步情境下只是讓程式碼更簡潔的選擇。",[81,1096],{},[64,1098,1100],{"id":1099},"常見問題callback-hell","常見問題：Callback Hell",[71,1102,1103,1104,1107],{},"當多個非同步操作彼此依賴，需要一個接一個執行時，callback 會層層嵌套，形成俗稱的 ",[74,1105,1106],{},"Callback Hell","。",[115,1109,1111],{"className":117,"code":1110,"language":119,"meta":120,"style":120},"fetchUser(id, (err, user) => {\n  fetchOrders(user.id, (err, orders) => {\n    fetchProducts(orders[0].id, (err, product) => {\n      console.log(product)\n      \u002F\u002F 再多一層依賴，程式碼就會往右無限縮排\n    })\n  })\n})\n",[122,1112,1113,1138,1170,1210,1226,1231,1238,1245],{"__ignoreMap":120},[125,1114,1115,1118,1121,1123,1125,1127,1129,1132,1134,1136],{"class":127,"line":128},[125,1116,1117],{"class":142},"fetchUser",[125,1119,1120],{"class":182},"(id",[125,1122,733],{"class":146},[125,1124,819],{"class":146},[125,1126,925],{"class":150},[125,1128,733],{"class":146},[125,1130,1131],{"class":150}," user",[125,1133,154],{"class":146},[125,1135,510],{"class":138},[125,1137,157],{"class":146},[125,1139,1140,1143,1145,1148,1150,1153,1155,1157,1159,1161,1164,1166,1168],{"class":127,"line":135},[125,1141,1142],{"class":142},"  fetchOrders",[125,1144,147],{"class":166},[125,1146,1147],{"class":182},"user",[125,1149,201],{"class":146},[125,1151,1152],{"class":182},"id",[125,1154,733],{"class":146},[125,1156,819],{"class":146},[125,1158,925],{"class":150},[125,1160,733],{"class":146},[125,1162,1163],{"class":150}," orders",[125,1165,154],{"class":146},[125,1167,510],{"class":138},[125,1169,157],{"class":146},[125,1171,1172,1175,1177,1180,1183,1186,1189,1191,1193,1195,1197,1199,1201,1204,1206,1208],{"class":127,"line":160},[125,1173,1174],{"class":142},"    fetchProducts",[125,1176,147],{"class":166},[125,1178,1179],{"class":182},"orders",[125,1181,1182],{"class":166},"[",[125,1184,1185],{"class":682},"0",[125,1187,1188],{"class":166},"]",[125,1190,201],{"class":146},[125,1192,1152],{"class":182},[125,1194,733],{"class":146},[125,1196,819],{"class":146},[125,1198,925],{"class":150},[125,1200,733],{"class":146},[125,1202,1203],{"class":150}," product",[125,1205,154],{"class":146},[125,1207,510],{"class":138},[125,1209,157],{"class":146},[125,1211,1212,1215,1217,1219,1221,1224],{"class":127,"line":170},[125,1213,1214],{"class":182},"      console",[125,1216,201],{"class":146},[125,1218,204],{"class":142},[125,1220,147],{"class":166},[125,1222,1223],{"class":182},"product",[125,1225,218],{"class":166},[125,1227,1228],{"class":127,"line":176},[125,1229,1230],{"class":131},"      \u002F\u002F 再多一層依賴，程式碼就會往右無限縮排\n",[125,1232,1233,1236],{"class":127,"line":195},[125,1234,1235],{"class":146},"    }",[125,1237,218],{"class":166},[125,1239,1240,1243],{"class":127,"line":221},[125,1241,1242],{"class":146},"  }",[125,1244,218],{"class":166},[125,1246,1247,1249],{"class":127,"line":229},[125,1248,224],{"class":146},[125,1250,218],{"class":182},[71,1252,1253],{},"這種寫法的問題：",[1255,1256,1257,1263,1272],"ul",{},[100,1258,1259,1262],{},[74,1260,1261],{},"可讀性差","：邏輯往右不斷縮排，難以追蹤執行順序",[100,1264,1265,1268,1269,1271],{},[74,1266,1267],{},"錯誤處理分散","：每一層都要自己判斷 ",[122,1270,925],{},"，容易漏掉",[100,1273,1274,1277],{},[74,1275,1276],{},"難以維護","：中途插入或調整步驟，容易牽一髮動全身",[81,1279],{},[64,1281,1283],{"id":1282},"和-promiseasyncawait-的關係","和 Promise、async\u002Fawait 的關係",[71,1285,1286,1287,1289,1290,1293,1294,1296],{},"Callback Hell 的問題，後來由 ",[74,1288,1068],{}," 改善（鏈式呼叫、統一錯誤處理），再由 ",[74,1291,1292],{},"async\u002Fawait"," 進一步簡化成接近同步的寫法。但這不代表 callback 被取代了——",[122,1295,1292],{}," 底層仍是 Promise，Promise 底層仍是靠 callback 機制運作（Event Loop 把 callback 塞進 Call Stack 執行）。",[71,1298,1299,1300,1302],{},"理解 callback 是什麼、它如何被 Event Loop 呼叫，是看懂 Promise 和 ",[122,1301,1292],{}," 底層運作的基礎。",[68,1304,1305],{},[71,1306,1307,1308,1310],{},"非同步的三個演進階段（callback → Promise → async\u002Fawait），可參考 ",[179,1309,50],{"href":51}," 一文。",[1312,1313,1314],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":120,"searchDepth":135,"depth":135,"links":1316},[1317,1318,1319,1320,1321,1322,1323,1324],{"id":66,"depth":135,"text":66},{"id":85,"depth":135,"text":86},{"id":310,"depth":135,"text":310},{"id":335,"depth":135,"text":336},{"id":535,"depth":135,"text":536},{"id":618,"depth":135,"text":619},{"id":1099,"depth":135,"text":1100},{"id":1282,"depth":135,"text":1283},"2026-07-09","精確定義 callback、判斷標準與常見誤解，介紹 JavaScript 中常用到 callback 的內建函數、Callback Hell 問題，以及它與 Promise、async\u002Fawait 的關係。","md",null,"\u002Fimages\u002Fdata-flow.jpg",{},{"title":14,"description":1326},"kDusKGejDlic-xec_l6YcMH0OTfJOZYZe5X4hkGdwMQ",[1334,1336],{"title":10,"path":11,"stem":12,"description":1335,"children":-1},"從「等待」的角度重新理解非同步存在的目的，並拆解 I\u002FO、計時器、使用者互動、執行緒同步、硬體協作等不同類型的等待。",{"title":18,"path":19,"stem":20,"description":1337,"children":-1},"拆解 AI coding agent 生態常見的四個擴充概念——Skill、MCP、Hook、Plugin，並以 Claude Code 為例說明實際配置方式與建議上手順序。",1784010808948]