[{"data":1,"prerenderedAt":607},["ShallowReactive",2],{"navigation":3,"\u002Farticles\u002Fasync-waiting":54,"\u002Farticles\u002Fasync-waiting-surround":604},[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":10,"author":56,"body":60,"date":596,"description":597,"extension":598,"externalUrl":599,"image":600,"meta":601,"minRead":391,"navigation":381,"path":11,"seo":602,"stem":12,"__hash__":603},"blog\u002Farticles\u002Fasync-waiting.md",{"name":57,"avatar":58},"Gary",{"src":59,"alt":57},"\u002Fimages\u002Fselfie.webp",{"type":61,"value":62,"toc":581},"minimark",[63,67,71,74,78,81,83,87,104,114,116,119,130,137,139,143,146,151,154,221,225,228,260,263,267,270,307,310,314,317,421,424,428,431,461,464,466,469,553,555,558,565,577],[64,65,66],"h2",{"id":66},"核心結論",[68,69,70],"p",{},"Async 存在的唯一目的，就是解決**「等待」**這件事——讓 CPU（或執行緒）在等待外部資源回應的空檔，不要傻傻卡住浪費掉，而是先去做別的事，等結果準備好了再回來處理。",[72,73],"hr",{},[64,75,77],{"id":76},"為什麼等待值得特別解決","為什麼「等待」值得特別解決",[68,79,80],{},"CPU 執行一個指令的時間，如果放大成 1 秒，等網路回應可能相當於等 3～9 年。這麼懸殊的速度差，如果同步等待，CPU 幾乎全部時間都在「浪費」——這才是 async 機制被發明出來的根本原因。",[72,82],{},[64,84,86],{"id":85},"反面驗證沒有等待的地方async-就沒意義","反面驗證：沒有等待的地方，async 就沒意義",[88,89,90,98],"ul",{},[91,92,93,97],"li",{},[94,95,96],"strong",{},"CPU 密集型任務","：全程都在運算，沒有空檔可以利用，async 幫不上忙，需要靠平行運算而不是 async",[91,99,100,103],{},[94,101,102],{},"RAM 存取","：延遲只有幾十奈秒，是 CPU 指令的內建流程，時間短到連排程切換的成本都划不來，不需要、也沒辦法做 async 處理",[105,106,107],"blockquote",{},[68,108,109,110,113],{},"I\u002FO 密集與 CPU 密集的速度數字、分界線與執行緒數怎麼開，可參考 ",[111,112,30],"a",{"href":31}," 一文。",[72,115],{},[64,117,118],{"id":118},"完整因果鏈",[120,121,126],"pre",{"className":122,"code":124,"language":125},[123],"language-text","外部資源比 CPU 慢很多（I\u002FO）\n      ↓\n如果同步等待 → CPU 大量時間閒置浪費\n      ↓\n用 async（非阻塞 I\u002FO + 事件通知）\n      ↓\nCPU 不用傻等，先做別的事，等通知了才回來處理\n      ↓\n單一執行緒也能高效應付大量並發的「等待中」任務\n","text",[127,128,124],"code",{"__ignoreMap":129},"",[68,131,132,133,136],{},"這條邏輯串起了為什麼需要 ",[111,134,135],{"href":15},"callback","／Promise（拿到「以後通知我」的入口）、為什麼 epoll 能用一條執行緒處理上萬連線（不用傻等）、為什麼 CPU 密集任務用不上 async（沒有等待可以省）——全部都是圍繞著「解決等待浪費」這個核心目的展開的。",[72,138],{},[64,140,142],{"id":141},"等待不等於io","「等待」不等於「I\u002FO」",[68,144,145],{},"I\u002FO 只是「等待」裡最大宗、最常被討論的一種，但不是唯一的一種。",[147,148,150],"h3",{"id":149},"_1-io-等待","1. I\u002FO 等待",[68,152,153],{},"等待外部裝置回應：網路、硬碟、鍵盤滑鼠等，需透過 OS 的 I\u002FO 子系統（driver、匯流排）。",[120,155,159],{"className":156,"code":157,"language":158,"meta":129,"style":129},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","fetch('\u002Fapi\u002Fdata')       \u002F\u002F 等網路\nfs.readFile('a.txt', cb) \u002F\u002F 等硬碟\n","js",[127,160,161,191],{"__ignoreMap":129},[162,163,166,170,174,178,182,184,187],"span",{"class":164,"line":165},"line",1,[162,167,169],{"class":168},"s2Zo4","fetch",[162,171,173],{"class":172},"sTEyZ","(",[162,175,177],{"class":176},"sMK4o","'",[162,179,181],{"class":180},"sfazB","\u002Fapi\u002Fdata",[162,183,177],{"class":176},[162,185,186],{"class":172},")       ",[162,188,190],{"class":189},"sHwdD","\u002F\u002F 等網路\n",[162,192,194,197,200,203,205,207,210,212,215,218],{"class":164,"line":193},2,[162,195,196],{"class":172},"fs",[162,198,199],{"class":176},".",[162,201,202],{"class":168},"readFile",[162,204,173],{"class":172},[162,206,177],{"class":176},[162,208,209],{"class":180},"a.txt",[162,211,177],{"class":176},[162,213,214],{"class":176},",",[162,216,217],{"class":172}," cb) ",[162,219,220],{"class":189},"\u002F\u002F 等硬碟\n",[147,222,224],{"id":223},"_2-計時器等待","2. 計時器等待",[68,226,227],{},"嚴格說不算 I\u002FO，但用同一套機制處理。",[120,229,231],{"className":156,"code":230,"language":158,"meta":129,"style":129},"setTimeout(() => {}, 1000) \u002F\u002F 純粹等時間過去，沒有資料進出任何裝置\n",[127,232,233],{"__ignoreMap":129},[162,234,235,238,240,243,247,250,254,257],{"class":164,"line":165},[162,236,237],{"class":168},"setTimeout",[162,239,173],{"class":172},[162,241,242],{"class":176},"()",[162,244,246],{"class":245},"spNyl"," =>",[162,248,249],{"class":176}," {},",[162,251,253],{"class":252},"sbssI"," 1000",[162,255,256],{"class":172},") ",[162,258,259],{"class":189},"\u002F\u002F 純粹等時間過去，沒有資料進出任何裝置\n",[68,261,262],{},"這裡沒有任何資料在「進出」，單純是等時鐘走到某個時間點。技術上不算 I\u002FO，但底層處理方式跟 I\u002FO 一模一樣：靠硬體計時器 + 事件通知，不需要 CPU 傻等，時間到了才觸發 callback。",[147,264,266],{"id":265},"_3-使用者互動等待","3. 使用者互動等待",[68,268,269],{},"廣義算 I\u002FO，但性質特殊。",[120,271,273],{"className":156,"code":272,"language":158,"meta":129,"style":129},"button.addEventListener('click', () => {})\n",[127,274,275],{"__ignoreMap":129},[162,276,277,280,282,285,287,289,292,294,296,299,301,304],{"class":164,"line":165},[162,278,279],{"class":172},"button",[162,281,199],{"class":176},[162,283,284],{"class":168},"addEventListener",[162,286,173],{"class":172},[162,288,177],{"class":176},[162,290,291],{"class":180},"click",[162,293,177],{"class":176},[162,295,214],{"class":176},[162,297,298],{"class":176}," ()",[162,300,246],{"class":245},[162,302,303],{"class":176}," {}",[162,305,306],{"class":172},")\n",[68,308,309],{},"鍵盤滑鼠技術上是輸入裝置，勉強可以歸類到 I\u002FO，但特殊之處在於：等待時間完全不可預測，甚至可能永遠不會發生（使用者可能永遠不點擊）。這跟「等網路封包，通常幾百毫秒內會有結果」的性質不太一樣。",[147,311,313],{"id":312},"_4-執行緒行程間的同步等待","4. 執行緒／行程間的同步等待",[68,315,316],{},"跟 I\u002FO 無關。",[120,318,320],{"className":156,"code":319,"language":158,"meta":129,"style":129},"\u002F\u002F 等另一條 Worker thread 算完\nworker.postMessage('start')\nworker.onmessage = (e) => {}\n\n\u002F\u002F 等多個非同步任務都完成\nawait Promise.all([task1(), task2()])\n",[127,321,322,327,348,376,383,389],{"__ignoreMap":129},[162,323,324],{"class":164,"line":165},[162,325,326],{"class":189},"\u002F\u002F 等另一條 Worker thread 算完\n",[162,328,329,332,334,337,339,341,344,346],{"class":164,"line":193},[162,330,331],{"class":172},"worker",[162,333,199],{"class":176},[162,335,336],{"class":168},"postMessage",[162,338,173],{"class":172},[162,340,177],{"class":176},[162,342,343],{"class":180},"start",[162,345,177],{"class":176},[162,347,306],{"class":172},[162,349,351,353,355,358,361,364,368,371,373],{"class":164,"line":350},3,[162,352,331],{"class":172},[162,354,199],{"class":176},[162,356,357],{"class":168},"onmessage",[162,359,360],{"class":176}," =",[162,362,363],{"class":176}," (",[162,365,367],{"class":366},"sHdIc","e",[162,369,370],{"class":176},")",[162,372,246],{"class":245},[162,374,375],{"class":176}," {}\n",[162,377,379],{"class":164,"line":378},4,[162,380,382],{"emptyLinePlaceholder":381},true,"\n",[162,384,386],{"class":164,"line":385},5,[162,387,388],{"class":189},"\u002F\u002F 等多個非同步任務都完成\n",[162,390,392,396,400,402,405,408,411,413,415,418],{"class":164,"line":391},6,[162,393,395],{"class":394},"s7zQu","await",[162,397,399],{"class":398},"sBMFI"," Promise",[162,401,199],{"class":176},[162,403,404],{"class":168},"all",[162,406,407],{"class":172},"([",[162,409,410],{"class":168},"task1",[162,412,242],{"class":172},[162,414,214],{"class":176},[162,416,417],{"class":168}," task2",[162,419,420],{"class":172},"()])\n",[68,422,423],{},"等待的對象是別的執行緒／行程，不是外部裝置——是在等「別人算完」或「別人放開資源」，但一樣需要非阻塞機制去處理，不然當前執行緒一樣會卡住浪費。",[147,425,427],{"id":426},"_5-硬體加速器等待","5. 硬體加速器等待",[68,429,430],{},"也不算傳統 I\u002FO。",[120,432,434],{"className":156,"code":433,"language":158,"meta":129,"style":129},"\u002F\u002F WebGPU 等 GPU 運算完成\nawait device.queue.onSubmittedWorkDone()\n",[127,435,436,441],{"__ignoreMap":129},[162,437,438],{"class":164,"line":165},[162,439,440],{"class":189},"\u002F\u002F WebGPU 等 GPU 運算完成\n",[162,442,443,445,448,450,453,455,458],{"class":164,"line":193},[162,444,395],{"class":394},[162,446,447],{"class":172}," device",[162,449,199],{"class":176},[162,451,452],{"class":172},"queue",[162,454,199],{"class":176},[162,456,457],{"class":168},"onSubmittedWorkDone",[162,459,460],{"class":172},"()\n",[68,462,463],{},"等 GPU 算完一段運算，這不是資料進出裝置，而是等另一個運算單元完成工作，性質上更接近「同步等待」而非「I\u002FO 等待」。",[72,465],{},[64,467,468],{"id":468},"分類總表",[470,471,472,488],"table",{},[473,474,475],"thead",{},[476,477,478,482,485],"tr",{},[479,480,481],"th",{},"等待類型",[479,483,484],{},"等待對象",[479,486,487],{},"算不算 I\u002FO？",[489,490,491,503,517,528,542],"tbody",{},[476,492,493,497,500],{},[494,495,496],"td",{},"網路／硬碟請求",[494,498,499],{},"外部裝置",[494,501,502],{},"✅ 是",[476,504,505,511,514],{},[494,506,507,508,510],{},"計時器（",[127,509,237],{},"）",[494,512,513],{},"時間本身",[494,515,516],{},"❌ 不算，機制類似",[476,518,519,522,525],{},[494,520,521],{},"使用者互動",[494,523,524],{},"人",[494,526,527],{},"⚠️ 廣義算（輸入裝置），性質特殊（不可預測）",[476,529,530,536,539],{},[494,531,532,533],{},"Worker \u002F ",[127,534,535],{},"Promise.all",[494,537,538],{},"其他執行緒／非同步任務",[494,540,541],{},"❌ 不算，是同步協調問題",[476,543,544,547,550],{},[494,545,546],{},"GPU 運算",[494,548,549],{},"另一個運算單元",[494,551,552],{},"❌ 不算，是協作等待",[72,554],{},[64,556,557],{"id":557},"結論",[68,559,560,561,564],{},"Async 要解決的核心問題，不是狹義的「I\u002FO」，而是更廣義的：",[94,562,563],{},"等待某個結果，而這個結果何時準備好，不由自己這條執行緒的程式碼控制","。I\u002FO 是這個大類別裡最常見、最具代表性的例子（因為速度差距最誇張），但計時器、使用者互動、執行緒同步、硬體協作，本質上都是同一種「等待」問題，只是等待的對象不同。",[105,566,567,572],{},[68,568,569,570,113],{},"非同步的三個演進階段與適用場景，可參考 ",[111,571,50],{"href":51},[68,573,574,575,113],{},"Callback 的完整定義與判斷標準，可參考 ",[111,576,14],{"href":15},[578,579,580],"style",{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":129,"searchDepth":193,"depth":193,"links":582},[583,584,585,586,587,594,595],{"id":66,"depth":193,"text":66},{"id":76,"depth":193,"text":77},{"id":85,"depth":193,"text":86},{"id":118,"depth":193,"text":118},{"id":141,"depth":193,"text":142,"children":588},[589,590,591,592,593],{"id":149,"depth":350,"text":150},{"id":223,"depth":350,"text":224},{"id":265,"depth":350,"text":266},{"id":312,"depth":350,"text":313},{"id":426,"depth":350,"text":427},{"id":468,"depth":193,"text":468},{"id":557,"depth":193,"text":557},"2026-07-09","從「等待」的角度重新理解非同步存在的目的，並拆解 I\u002FO、計時器、使用者互動、執行緒同步、硬體協作等不同類型的等待。","md",null,"\u002Fimages\u002Fdata-flow.jpg",{},{"title":10,"description":597},"mhHhbQT8ACnst4P_atT7-cbMU74ylFavxsYs-y3ZMYE",[599,605],{"title":14,"path":15,"stem":16,"description":606,"children":-1},"精確定義 callback、判斷標準與常見誤解，介紹 JavaScript 中常用到 callback 的內建函數、Callback Hell 問題，以及它與 Promise、async\u002Fawait 的關係。",1784010808948]