JAVASCRIPT V2 · ASYNC BRIDGE · 13
Async Bridge:真正困難的不是 await,而是「現在還沒有答案」
到目前為止,多數程式都是「呼叫 function,立刻拿到 return value」。Network、timer、檔案與使用者互動打破這個直覺:工作現在開始,結果之後才完成。這一課先建立時間模型,再進 Promise。
Learning outcomes
- 區分 synchronous result 與 deferred result。
- 理解 callback / Promise 都是在表達「之後再繼續」。
- 能說明等待外部工作與阻塞 main thread 的差異。
- 能把 async UI 建模成 idle / loading / success / error。
- 能預測最基本的同步與 timer execution ordering。
1. Why now:同步 function 有一個隱藏前提
function double(n) {
return n * 2;
}
const result = double(21);
console.log(result); // 42
呼叫結束時,答案已經存在。但下面這些工作沒有這個保證:
- HTTP request
- timer
- 讀取檔案
- 等待使用者 click
如果你仍用「下一行一定已經拿到答案」的心智模型,async code 很快會變成猜測。
2. Mental model:Start now, continue later
現在:
start work
↓
沒有最終結果
↓
目前 JavaScript 結束這一輪工作
之後:
external work completes
↓
continuation becomes runnable
↓
JavaScript 繼續處理結果
3. Worked Example A:setTimeout 並不是 sleep
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
預測後再執行。結果是:
A
C
B
setTimeout(..., 0) 的意思不是「現在立刻插隊」,而是「最早在目前同步工作結束後,才有機會執行 callback」。
4. Worked Example B:錯誤的同步假設
let user;
setTimeout(() => {
user = { name: "Amy" };
}, 100);
console.log(user.name);
問題不是 timer 太慢,而是 console.log 執行時 user 尚未被指定。把 timeout 從 100 改成 0 也沒有修正資料依賴。
setTimeout(() => {
const user = { name: "Amy" };
console.log(user.name);
}, 100);
真正的修正是把「需要結果的 continuation」放到結果可用之後。
5. Callback 的價值與限制
function loadUser(done) {
setTimeout(() => {
done({ name: "Amy" });
}, 100);
}
loadUser(user => {
console.log(user.name);
});
callback 能表達「完成後做什麼」,但多層 success/error/cancellation 很快會難以組合。Promise 就是下一課要用的標準化結果容器。
6. Worked Example C:UI state 不應只有「有資料 / 沒資料」
const state = {
status: "idle",
data: null,
error: null
};
| status | 畫面 | 代表 |
|---|---|---|
| idle | 尚未載入 | 工作還沒開始 |
| loading | Loading... | 工作已開始、結果未到 |
| success | 資料 | 結果成功 |
| error | 錯誤訊息 | 工作失敗 |
這個 state machine 會一路出現在 fetch、React Query、Android coroutine 與後端 job。
7. Common mistakes
A. 用固定 delay 猜資料何時回來
setTimeout(..., 1000) 不是 dependency management。網路可能 50ms,也可能 5s。
B. 把 async 當成「多執行緒」同義詞
Browser 可以在外部處理 timer/network,但 JS callback 何時進 main thread 仍受 event loop 排程。
C. 只畫 success UI
沒有 loading/error 時,使用者會把「正在等」誤認成「壞掉」。
D. 用 0ms timer 當立即呼叫
callback 仍必須等目前 stack 結束。
8. Debug evidence:先問「結果在哪個時間點才存在?」
console.log("before request");
startAsyncWork(result => {
console.log("result arrived", result);
});
console.log("after start");
如果 bug 是「讀到 undefined」,最有價值的 evidence 往往不是多印一次 value,而是記錄:
- 工作何時 start?
- 依賴結果的 code 何時 run?
- 結果何時 arrive?
- 是否存在先後順序假設?
9. Guided exercise:把錯誤 timing 修正
let total;
setTimeout(() => {
total = 120;
}, 10);
console.log("Total:", total);
要求:不靠增加 delay,讓輸出一定發生在 total 可用之後。先用 callback 修,再在 42 課改成 Promise。
10. Independent exercise:建立四態 loader
設計一個 state object,模擬 request 開始、成功與失敗,並寫出 renderStatus(state)。每個 transition 都要能從 Console 看見。
11. Challenge:哪個先?
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");
setTimeout(() => console.log("4"), 0);
先寫 prediction,再執行,再用一句話解釋為什麼不是 1-2-3-4。Event Loop 課會加入 Promise microtask,讓順序更有挑戰。
Project checkpoint:Grade Tracker 進入 Loading state
idle
↓ click Load
loading
↓ result
success
或
loading
↓ failure
error
這一課只建立 state machine,不急著接真 API。下一課 42 才把 Promise / async / await 接進來。
12. Vocabulary / Summary
- synchronous:目前呼叫完成時結果已可用。
- deferred result:結果在未來某個時間點才完成。
- callback:之後被呼叫的 function。
- continuation:目前工作結束後,未來要恢復的後續邏輯。
- blocking:目前 execution context 被工作佔住,無法讓出執行權。
13. Further reading
Knowledge check · Mastery
- 解釋為什麼 0ms timer 仍不會插到同步 code 中間。
- 指出「改成等 2 秒」為何不是可靠 async dependency 解法。
- 畫出 idle → loading → success/error。
- 完成頁面下方 Execution Trace practice。