首頁 >web前端 >js教程 >技術面試問題 - 部分事件循環

技術面試問題 - 部分事件循環

Mary-Kate Olsen
Mary-Kate Olsen原創
2024-11-29 11:45:18161瀏覽

介紹

大家好!

今天,正如標題所說,我將討論事件循環。

這不是面試官經常直接詢問的話題(我只記得有兩次他們讓我解釋事件循環)。但是,在大多數採訪中,他們會提出與之相關的問題。例如:

  • “如果我這樣做…應該有什麼行為?”
  • “如果我的程式碼如下所示,輸出會是什麼?”
  • “為什麼這段程式碼會產生這樣的輸出?”

如果您了解事件循環的工作原理,那麼所有這些問題都會更容易回答。

老實說:這個話題不是我最喜歡的。我更喜歡詢問有關程式碼行為的問題,而不是解釋事件循環如何連續工作 10 分鐘。 ?

Technical Interview Questions - Part  Event Loop

讓我們開始吧! ?

## 問題
1.什麼是事件循環?
2. 範例


什麼是事件循環?

簡短回答:

事件循環負責處理 JavaScript 執行階段中的非同步任務。

說實話,我認為這個答案不足以滿足面試官詢問事件循環的好奇心。因此,在這篇文章中,我想更深入地探討這個主題。

不僅僅是了解概念,了解如何它的工作原理也很重要。這就是為什麼我在最後添加了一些示例。

Technical Interview Questions - Part  Event Loop

理論

什麼是事件循環?

JavaScript 有一個基於事件循環的執行時間,負責處理任務。每種語言都有獨特的運行時,需要注意的重要一點是 JavaScript 是單執行緒

單線程是什麼意思?

單執行緒意味著JavaScript 一次只能處理一項任務。這就是為什麼事件循環在 JavaScript 中如此重要;儘管存在單執行緒限制,它仍然有助於有效地管理任務。

運行時的組成部分

為了更好地理解事件循環,我們先來看看它的主要組成部分:

Technical Interview Questions - Part  Event Loop

呼叫堆疊

呼叫堆疊是一種資料結構,用來追蹤我們所呼叫的函數。你可以把它想像成一堆盤子:當一個函數被呼叫時,它被加到堆疊中,當它完成時,它被從堆疊中刪除。

呼叫堆疊遵循LIFO(後進先出) 原則,這意味著JavaScript 按照函數的堆疊順序執行函數— 從最上面的項目到底部,一個位於一次(記住,JavaScript 是單線程的)。

佇列

在 JavaScript 的執行時間中,我們有佇列,它保存要處理的任務清單。這些佇列中的任務會等待,直到呼叫堆疊為空。

  • 任務佇列(或回調佇列):此佇列儲存諸如 setTimeout() 和 setInterval() 呼叫之類的任務。這裡的任務是在呼叫堆疊為空並且微任務佇列中的所有任務都處理完之後進行處理的。在 MDN 上查看儲存在此佇列中的任務的更多範例。

  • 微任務佇列: 此佇列的優先權高於任務佇列。它包括微任務(例如 Promise 回調)和非同步函數(例如 process.nextTick() 和非同步函數)。

任務佇列FIFO(先進先出)為基礎工作,這表示任務按照新增的順序進行處理,但僅在微任務之後進行佇列已空。

事件循環

事件循環是一種管理非同步程式碼執行的機制。它觀察呼叫堆疊以及呼叫堆疊和佇列(任務佇列和微任務佇列)之間的協調,以保持程式碼順利運行。

它是如何運作的?

讓我們一步步過一遍事件循環過程。請參閱下圖以獲得直觀表示。

Technical Interview Questions - Part  Event Loop

在此範例中:

  • 呼叫堆疊只有一個函數。
  • 微任務佇列有兩個訊息。
  • 任務佇列有訊息。

第 1 步:處理呼叫堆疊

  1. 事件循環從查看呼叫堆疊開始。
  2. 它在堆疊中找到一個函數並開始執行它。
  3. 此函數完成後,就會從呼叫堆疊中刪除。

Technical Interview Questions - Part  Event Loop

步驟2:處理微任務隊列

  1. 呼叫堆疊為空後,事件循環會檢查微任務佇列
  2. 它從微任務佇列取得第一個訊息並將其推送到呼叫堆疊來執行。

Technical Interview Questions - Part  Event Loop

  1. 函數運行,一旦完成,就會從呼叫堆疊中刪除。
  2. 事件循環然後移動到微任務佇列中的下一則訊息並重複此過程。
  3. 這會持續到微任務佇列中不再有消息為止。

Technical Interview Questions - Part  Event Loop

第三步:處理任務隊列

  1. 一旦呼叫堆疊微任務佇列都為空,事件循環就會轉向任務佇列
  2. 它選擇任務佇列中的第一個訊息並將其新增至呼叫堆疊
  3. 函數運行,完成後,它會從呼叫堆疊中刪除。
  4. 事件循環將對任務佇列中的每個任務繼續此過程,確保所有任務都被一一處理。

Technical Interview Questions - Part  Event Loop

依照這個順序-呼叫堆疊,然後微任務佇列,最後任務佇列-事件循環幫助JavaScript有效處理非同步程式碼,即使在它的單線程環境。


範例

現在我們了解了事件循環的工作原理以及任務的優先順序如何確定,讓我們來看一些範例。

實施例1

const a = new Promise(function showA(resolve){
  console.log('A');
  resolve('B');
});

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

a.then(function showB(b) {
  console.log(b);
});

const d = function showD() {
  console.log('D');
};

d();

在繼續之前,試著考慮一下輸出的順序。

✨你期望它是什麼? ✨

讓我們分解程式碼的每個部分來理解為什麼我們會得到這個輸出。

1。創造承諾

const a = new Promise(function showA(resolve) {
  console.log('A');
  resolve('B');
});
  • 在這裡,我們建立一個帶有回調函數的新 Promise。
  • 在此函數內,console.log('A') 會立即執行,因此 "A" 會列印到控制台。
  • 記錄「A」後,promise 被解析為值「B」。
  • JavaScript 識別出有一個.then 回調(即showB),一旦主調用堆疊清除,該回調應該運行,因此它將showB 添加到微任務隊列(因為Promise 解析會去那裡) 。

2。 setTimeout 呼叫

setTimeout(function showC() {
  console.log('C');
}, 0);
  • setTimeout 函數安排 showC 在 0 毫秒後運行。
  • JavaScript 將 showC 放入任務佇列中,因為它是一個基於計時器的函數。

3。 a.then 回呼

const a = new Promise(function showA(resolve){
  console.log('A');
  resolve('B');
});

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

a.then(function showB(b) {
  console.log(b);
});

const d = function showD() {
  console.log('D');
};

d();
  • 這行為我們在上一個步驟中已經解決的 Promise 註冊了一個 .then 處理程序 (resolve('B'))。
  • 由於 Promise 已解決,showB(.then 回調)被加入到 微任務佇列

4。定義 d

const a = new Promise(function showA(resolve) {
  console.log('A');
  resolve('B');
});
  • 這一行只是定義了函數 showD 但尚未執行它,所以這裡什麼也沒有發生。

5。呼叫 d()

setTimeout(function showC() {
  console.log('C');
}, 0);
  • 現在,我們呼叫 d(),它被加到 呼叫堆疊 並執行。 這會產生 console.log('D'),因此 "D" 會列印到控制台。

最終輸出順序:

a.then(function showB(b) {
  console.log(b);
});

GIF供參考

Technical Interview Questions - Part  Event Loop
互動範例

實施例2

const d = function showD() {
  console.log('D');
};

再花點時間考慮輸出的順序。

✨你期望它是什麼? ✨

讓我們來解釋一下......

1。記錄「開始!」

d();
  • 這行程式碼被加到呼叫堆疊並立即執行。
  • 結果,「開始!」 被印到控制台。
  1. 設定超時調用
A
D
B
C
  • setTimeout 函數安排 showTimeout 在 0 毫秒後運行。
  • JavaScript 將 showTimeout 放置在 任務佇列 中,因為它是一個基於計時器的函數。

3。承諾決議

console.log("Start!");

setTimeout(function showTimeout() {
  console.log("Timeout!");
}, 0);

Promise.resolve("Promise!")
  .then(function showPromise(res) {
    console.log(res);
  });

console.log("End!");
  • 承諾立即解決,值為「Promise!」。
  • JavaScript 將 showPromise(.then 回呼)放入 微任務佇列,因為 Promise 在解析後會進入微任務佇列。

4。記錄「結束!」

console.log("Start!");
  • 這行程式碼被加到呼叫堆疊並立即執行。
  • 結果,「結束!」 被印到控制台。

最終輸出順序:

setTimeout(function showTimeout() {
  console.log("Timeout!");
}, 0);

GIF供參考

Technical Interview Questions - Part  Event Loop
互動範例

結尾

本章並不太長,但我希望這些範例可以幫助您理解事件循環的工作原理。

我強烈建議嘗試互動頁面來分析其他範例。在該頁面上進行操作可以更輕鬆地理解正在運行的事件循環。

非常感謝大家對我之前貼文的喜愛!

下週見! ?

再見

Technical Interview Questions - Part  Event Loop

以上是技術面試問題 - 部分事件循環的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn