← JavaScript Learning Path

JAVASCRIPT V2 · ASYNC BRIDGE · 13

Async Bridge:真正困難的不是 await,而是「現在還沒有答案」

到目前為止,多數程式都是「呼叫 function,立刻拿到 return value」。Network、timer、檔案與使用者互動打破這個直覺:工作現在開始,結果之後才完成。這一課先建立時間模型,再進 Promise。

Learning outcomes

1. Why now:同步 function 有一個隱藏前提

function double(n) {
  return n * 2;
}

const result = double(21);
console.log(result); // 42

呼叫結束時,答案已經存在。但下面這些工作沒有這個保證:

如果你仍用「下一行一定已經拿到答案」的心智模型,async code 很快會變成猜測。

2. Mental model:Start now, continue later

現在:
start work
  ↓
沒有最終結果
  ↓
目前 JavaScript 結束這一輪工作

之後:
external work completes
  ↓
continuation becomes runnable
  ↓
JavaScript 繼續處理結果
非同步不等於平行執行所有 JavaScript。 它首先代表「目前不能完成的工作,不必用 blocking 等到它完成」。

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尚未載入工作還沒開始
loadingLoading...工作已開始、結果未到
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,而是記錄:

  1. 工作何時 start?
  2. 依賴結果的 code 何時 run?
  3. 結果何時 arrive?
  4. 是否存在先後順序假設?

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

13. Further reading

Knowledge check · Mastery

  1. 解釋為什麼 0ms timer 仍不會插到同步 code 中間。
  2. 指出「改成等 2 秒」為何不是可靠 async dependency 解法。
  3. 畫出 idle → loading → success/error。
  4. 完成頁面下方 Execution Trace practice。