跳至主要内容

Event Loop

提示

這是什麼:因為 JS 一次只能做一件事(單執行緒)

JavaScript 是單執行緒 (同步)​

JavaScript 一次只能做一件事情,並且是由上而下順序讀取代碼,例如:阿美中午午休要去買午餐,還想順便去繳個手機費

  1. 麵攤買個麵
  2. 繳手機費
  3. 麵包店買個明天的早餐

JS 會很單純的一步一步等每一件事情執行完成才執行下一步

執行堆疊(Execution Stack)​

JS 是使用執行堆疊 (也稱 Call Stack) 來追蹤函式的呼叫。想像是一疊盤子,後進先出(LIFO):

Call Stack (由下往上堆疊)
┌─────────────────────┐
│ 麵包店買明天早餐 │ ← 最後執行 (Top)
├─────────────────────┤
│ 繳手機費 │
├─────────────────────┤
│ 去手機店抽號碼牌 │
├─────────────────────┤
│ 等麵好 │
├─────────────────────┤
│ 買個麵 │ ← 最先執行 (Bottom)
└─────────────────────┘

用 Function 呈現​

function 買個麵() {
console.log('1. 買個麵');
}

function 等麵好() {
console.log('2. 等麵好');
}

function 去手機店抽號碼牌() {
console.log('3. 去手機店抽號碼牌');
}

function 繳手機費() {
去手機店抽號碼牌(); // 呼叫另一個函式
console.log('4. 繳手機費');
}

function 麵包店買明天早餐() {
console.log('5. 麵包店買明天早餐');
}

// 開始執行
function 阿美的午休() {
買個麵();
等麵好();
繳手機費();
麵包店買明天早餐();
}

阿美的午休();

Call Stack 變化過程:

步驟 1: 呼叫 阿美的午休()
┌─────────────────┐
│ 阿美的午休() │
└─────────────────┘

步驟 2: 呼叫 買個麵()
┌─────────────────┐
│ 買個麵() │
├─────────────────┤
│ 阿美的午休() │
└─────────────────┘

步驟 3: 買個麵() 執行完畢,移出堆疊
┌─────────────────┐
│ 阿美的午休() │
└─────────────────┘

步驟 4: 呼叫 等麵好()
┌─────────────────┐
│ 等麵好() │
├─────────────────┤
│ 阿美的午休() │
└─────────────────┘

步驟 5: 呼叫 繳手機費(),繳手機費() 又呼叫 去手機店抽號碼牌()
┌─────────────────┐
│ 去手機店抽號碼牌() │ ← 最後進入
├─────────────────┤
│ 繳手機費() │
├─────────────────┤
│ 阿美的午休() │
└─────────────────┘

步驟 6: 去手機店抽號碼牌() 執行完畢,回到 繳手機費()
┌─────────────────┐
│ 繳手機費() │
├─────────────────┤
│ 阿美的午休() │
└─────────────────┘

步驟 7: 繳手機費() 執行完畢,回到 阿美的午休()
┌─────────────────┐
│ 阿美的午休() │
└─────────────────┘

步驟 8: 呼叫 麵包店買明天早餐()
┌─────────────────┐
│ 麵包店買明天早餐() │
├─────────────────┤
│ 阿美的午休() │
└─────────────────┘

步驟 9: 全部執行完畢,堆疊清空
[ 空的 Call Stack ]

JavaScript 外面的世界​

當今天 JS 遇到了一個非同步操作 (例如 setTimeout),他不會等待,他會將這個任務交給對應的 Web API (瀏覽器或 Node.js) 來處理,然後 JS 就繼續自己的 Call Stack

Web API 做了什麼?​

今天瀏覽器收到了任務 (setTimeout, DOM Events, fetch()(AJAX)) 完成後,他不會直接去吵 JS ,會將完成的任務放在 Event Queue(事件佇列) 排隊,等 JS 的 Call Stack 空了後再丟過去

Event Loop 事件循環 (非同步)​

而 Event Loop 就是一個不停檢查 Call Stack 有沒有空,如果空了就把 Event Queue 取出第一個任務放到 Call Stack 裡執行

練習題 (請回答會印出什麼資料)​

console.log('1');

setTimeout(function() {
console.log('2');
},0);

console.log('3')

答案

1;
3;
2;

上面寫了『同步』和『非同步』是指什麼?​

像 JavaScript 這樣一次做一件事,做完一件再做下一件就是屬於同步,但如果今天瀏覽網站送出一個表單後,整個網站會像當機一樣停住不能做任何事,直到伺服器回應了『表單送出成功』才能再繼續瀏覽網站就很不科學, 所以就出現了非同步的慨念,表單送出後不會整個網站停住可以繼續瀏覽,而是由瀏覽器或其他環境來處理非同步的狀況