跳至主要内容

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