
Async 到底在解決什麼問題?
從「等待」的角度重新理解非同步存在的目的,並拆解 I/O、計時器、使用者互動、執行緒同步、硬體協作等不同類型的等待。
Gary
核心結論
Async 存在的唯一目的,就是解決**「等待」**這件事——讓 CPU(或執行緒)在等待外部資源回應的空檔,不要傻傻卡住浪費掉,而是先去做別的事,等結果準備好了再回來處理。
為什麼「等待」值得特別解決
CPU 執行一個指令的時間,如果放大成 1 秒,等網路回應可能相當於等 3~9 年。這麼懸殊的速度差,如果同步等待,CPU 幾乎全部時間都在「浪費」——這才是 async 機制被發明出來的根本原因。
反面驗證:沒有等待的地方,async 就沒意義
- CPU 密集型任務:全程都在運算,沒有空檔可以利用,async 幫不上忙,需要靠平行運算而不是 async
- RAM 存取:延遲只有幾十奈秒,是 CPU 指令的內建流程,時間短到連排程切換的成本都划不來,不需要、也沒辦法做 async 處理
I/O 密集與 CPU 密集的速度數字、分界線與執行緒數怎麼開,可參考 I/O 密集 vs CPU 密集:執行緒數該開多少? 一文。
完整因果鏈
外部資源比 CPU 慢很多(I/O)
↓
如果同步等待 → CPU 大量時間閒置浪費
↓
用 async(非阻塞 I/O + 事件通知)
↓
CPU 不用傻等,先做別的事,等通知了才回來處理
↓
單一執行緒也能高效應付大量並發的「等待中」任務
這條邏輯串起了為什麼需要 callback/Promise(拿到「以後通知我」的入口)、為什麼 epoll 能用一條執行緒處理上萬連線(不用傻等)、為什麼 CPU 密集任務用不上 async(沒有等待可以省)——全部都是圍繞著「解決等待浪費」這個核心目的展開的。
「等待」不等於「I/O」
I/O 只是「等待」裡最大宗、最常被討論的一種,但不是唯一的一種。
1. I/O 等待
等待外部裝置回應:網路、硬碟、鍵盤滑鼠等,需透過 OS 的 I/O 子系統(driver、匯流排)。
fetch('/api/data') // 等網路
fs.readFile('a.txt', cb) // 等硬碟
2. 計時器等待
嚴格說不算 I/O,但用同一套機制處理。
setTimeout(() => {}, 1000) // 純粹等時間過去,沒有資料進出任何裝置
這裡沒有任何資料在「進出」,單純是等時鐘走到某個時間點。技術上不算 I/O,但底層處理方式跟 I/O 一模一樣:靠硬體計時器 + 事件通知,不需要 CPU 傻等,時間到了才觸發 callback。
3. 使用者互動等待
廣義算 I/O,但性質特殊。
button.addEventListener('click', () => {})
鍵盤滑鼠技術上是輸入裝置,勉強可以歸類到 I/O,但特殊之處在於:等待時間完全不可預測,甚至可能永遠不會發生(使用者可能永遠不點擊)。這跟「等網路封包,通常幾百毫秒內會有結果」的性質不太一樣。
4. 執行緒/行程間的同步等待
跟 I/O 無關。
// 等另一條 Worker thread 算完
worker.postMessage('start')
worker.onmessage = (e) => {}
// 等多個非同步任務都完成
await Promise.all([task1(), task2()])
等待的對象是別的執行緒/行程,不是外部裝置——是在等「別人算完」或「別人放開資源」,但一樣需要非阻塞機制去處理,不然當前執行緒一樣會卡住浪費。
5. 硬體加速器等待
也不算傳統 I/O。
// WebGPU 等 GPU 運算完成
await device.queue.onSubmittedWorkDone()
等 GPU 算完一段運算,這不是資料進出裝置,而是等另一個運算單元完成工作,性質上更接近「同步等待」而非「I/O 等待」。
分類總表
| 等待類型 | 等待對象 | 算不算 I/O? |
|---|---|---|
| 網路/硬碟請求 | 外部裝置 | ✅ 是 |
計時器(setTimeout) | 時間本身 | ❌ 不算,機制類似 |
| 使用者互動 | 人 | ⚠️ 廣義算(輸入裝置),性質特殊(不可預測) |
Worker / Promise.all | 其他執行緒/非同步任務 | ❌ 不算,是同步協調問題 |
| GPU 運算 | 另一個運算單元 | ❌ 不算,是協作等待 |
結論
Async 要解決的核心問題,不是狹義的「I/O」,而是更廣義的:等待某個結果,而這個結果何時準備好,不由自己這條執行緒的程式碼控制。I/O 是這個大類別裡最常見、最具代表性的例子(因為速度差距最誇張),但計時器、使用者互動、執行緒同步、硬體協作,本質上都是同一種「等待」問題,只是等待的對象不同。
非同步的三個演進階段與適用場景,可參考 同步 vs 非同步 一文。
Callback 的完整定義與判斷標準,可參考 Callback 是什麼? 一文。