JavaScript 的非同步機制是許多新手卡關的地方,像是 setTimeout
、Promise
、async/await
等名詞看起來熟悉,但實際運作卻令人迷惑。這篇文章將循序漸進介紹非同步流程,幫助你了解背後邏輯及日常開發會用到的場景,以及推薦有帥哥的介紹影片。
一、同步與非同步:理解程式的執行順序
為什麼需要非同步?
在程式執行中,有時候某些操作(例如向伺服器取得資料)需要等待結果,但我們不希望整個程式暫停在原地等待。這就是非同步機制的用意。想像你開發一個網站,使用者點擊「取得報價」後,網站會向伺服器請求資料。這個過程是非同步的,因為你不希望整個頁面卡住,只是等回應。🔍 同步 vs 非同步
- 同步:每行程式碼照順序執行,等前一行做完才能繼續。
- 非同步:某些操作可以先暫存,等結果出來再繼續執行。
同步:
┌─────┐ → ┌─────┐ → ┌─────┐
│ A │ │ B │ │ C │
非同步:
┌─────┐ ──> 等待(後續執行)
│ A │
┌─────┐ → ┌─────┐
│ C │ │ D │
↑執行B回來
console.log("A");
console.log("B");
console.log("C");
🔁 結果:A → B → C(同步)
console.log("A");
setTimeout(() => {
console.log("B");
}, 1000);
console.log("C");
結果:A → C → B(非同步)
二、Call stack 和 Event loop:程式是怎麼跑的?
我個人覺得這裡很難懂QQ 因為講者是我的菜,所以我挑了這個影片跟大家介紹
所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU
JavaScript 採用「單執行緒」,表示一次只能處理一個任務。那非同步要怎麼執行?這就涉及 call stack(呼叫堆疊)和 event loop(事件循環)。
程式碼範例:
console.log("Start");
setTimeout(() => {
console.log("Inside setTimeout");
}, 0);
console.log("End");
🔁 輸出結果:
Start End Inside setTimeout
🔍 運作流程
除了在瀏覽器運行程式碼外,還有一個區域叫 web APIs, 可參考影片的11:45處。
"Start"
放入 call stack → 執行 → 移除 (執行完一列就會移除)setTimeout
被登記進 Web APIs 排隊→ 等候回傳"End"
放入 call stack → 執行 → 移除- Event loop 檢查 callback queue → 將
"Inside setTimeout"
從隊伍抓出來,加入 call stack → 執行"Inside setTimeout"
- 更簡單的想,Event loop 就是檢查「還有沒有未跑的程式在排隊」
📦 應用情境
如果我設計了一個 Threads 資料提取程式,資料處理不能卡住主流程,所以要讓它們進入等待區,待主程式清空後再執行。

截圖自 Youtube 影片,注意 event loop 的迴圈運作。

三、Callback:非同步的起點,但容易造成混亂
Callback 是把函式當作參數傳入另一個函式裡,等它完成後再執行。這種方式簡單但容易造成「巢狀結構太深」,又稱 Callback Hell。
🧪 範例:
function fetchData(callback) {
setTimeout(() => {
callback("資料拿到了");
}, 1000);
}
fetchData((data) => {
console.log(data);
});
商業應用:如果有多個步驟要執行(例如 Threads API 抓資料 → 分類 → 上傳),用 Callback 一層層包起來,錯誤處理會變得混亂。
四、Promise:解決 Callback 混亂的更好方法
Promise(承諾物件) 是 JavaScript 提供的非同步處理方式,它有三種狀態:
- Pending(等待中)
- Resolved(成功)
- Rejected(失敗)
🧪 範例:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功取得資料");
}, 1000);
});
}
fetchData()
.then((res) => console.log(res))
.catch((err) => console.error(err));
🧾 輸出結果:
成功取得資料
🗂️ 用途情境: 在我的 Threads 資料處理流程中,Promise 可以讓我更清楚地分隔資料抓取、處理、儲存等邏輯,也方便加入錯誤攔截機制。
五、async/await + try/catch:非同步流程更好寫
async/await 是 Promise 的語法糖(懶人寫法),讓你可以用看起來像同步的方式寫非同步程式碼。搭配 try/catch 可以完整捕捉錯誤。
🧪 範例:
async function run() {
try {
const result = await fetchData();
console.log(result);
} catch (error) {
console.error("發生錯誤:", error);
}
}
📌 優點說明:
- 程式碼結構清晰
- 容易閱讀與維護
- 錯誤處理一致性高
💡 商業應用場景: 我在使用週末執行自動排程,從 Threads API 抓資料 → 分類 → 儲存至 Google Sheets,再通知用戶。用 async/await
寫這段流程,可以保證每個步驟順利連接,錯誤也能即時攔截。
