← Writing

JavaScript] - Event Loop

今天,我想來點 Event Loop (事件循環)! 為什麼會有 Event Loop (事件循環)? 在正式說明之前,先來理解一下,為何會有 Event Loop (事件循環) 吧! JavaScript 是一種單執行緒(singlethreaded)的程式語言,也就是在任何給定的時間只能執行一個...

javascript

今天,我想來點 Event Loop (事件循環)!

為什麼會有 Event Loop (事件循環)?

在正式說明之前,先來理解一下,為何會有 Event Loop (事件循環) 吧!

JavaScript 是一種單執行緒(single-threaded)的程式語言,也就是在任何給定的時間只能執行一個任務。 換句話說,當 JavaScript 執行一段代碼時,其他的代碼或任務必須等待。 這個單執行緒在某些情況下可能會成為限制,尤其是當有耗時的操作需要處理時,例如網絡請求。

如果 JavaScript 在執行這些耗時操作的同時,主執行緒阻塞的話,將導致應用程序變得沒反應,甚至可能出現凍結的情況,這會給使用者帶來不好的體驗。

為了解決這個問題,JavaScript 引入了 Event Loop (事件循環) 機制。 事件循環使得 JavaScript 能夠處理非同步操作,而不會阻塞主執行緒。

當一個耗時的操作(例如網絡請求)被觸發時, JavaScript 將它交給外部的系統(如瀏覽器或 Node.js)處理,同時繼續執行其他代碼。 當操作完成並準備好時,一個回調函數將被放入事件佇列(Message Queue)中。 事件循環定期檢查佇列,並將回調函數執行,這樣就實現了非同步操作的處理,而不會阻塞主執行緒。

這種機制使得 JavaScript 能夠同時處理多個任務,保持應用程序的反應性,從而提供更好的使用者體驗。


Event Loop (事件循環) 的組成

Event Loop (事件循環) 有以下重要觀念:

  1. Heap (堆): Heap (堆) 用於存儲複雜的數據。堆的特點是其大小比較靈活,可以根據需要動態分配和釋放內存。通常用於存儲動態生成的數據,例如用戶輸入或在運行時創建的對象。

  2. Stack (棧): Stack (棧) 特點是 後進先出(Last-In, First-Out,LIFO) 的原則。當一個函數被調用時,其局部變數和函數調用信息被壓入棧的頂部。當函數執行完成後,這些信息被彈出,直到棧被清空。棧主要用於跟蹤函數調用和局部變數。

  3. Queue (隊列): Queue (隊列) 特點是 先進先出(First-In, First-Out,FIFO) 的原則。在 JavaScript 的執行環境中,等待處理的任務通常會被放在隊列中,例如事件處理程序、非同步操作的回調函數等。最早進入隊列的任務會首先被處理。

    MDN - Event Loop

Event Loop (事件循環) 的工作方式:

  1. 檢查 Call Stack(執行棧): 事件循環首先檢查執行棧是否為空。 執行棧是一個紀錄當前執行上下文的結構,如果它為空,表示目前沒有待處理的同步代碼。

  2. 檢查 Task Queue(任務隊列): 如果執行棧為空,事件循環接下來會檢查任務隊列。 任務隊列是用於存儲待處理的非同步操作的佇列,通常包括定時器、事件處理程序回調等。

  3. 將下一個任務的回調函數推入執行棧: 如果執行棧和任務隊列都不為空,事件循環將從隊列中取出下一個任務的回調函數,並將它推入執行棧中開始執行。 這表示開始處理一個非同步操作。

  4. 執行回調函數: 執行棧中的回調函數開始執行,處理相應的任務或事件。 這可以是網絡請求的回調、定時器的回調、按鈕點擊事件的回調等等。

  5. 檢查執行棧和任務隊列: 回調函數執行完成,事件循環會再次檢查執行棧和任務隊列。 如果執行棧為空,且隊列中還有待處理的任務,則重複上述步驟, 將下一個任務的回調函數推入執行棧,繼續處理其他非同步操作。

  6. 重複運行: 事件循環將以上步驟不斷重複,確保 JavaScript 能夠同時處理多個任務和事件,從而保持程式的反應性。

Brief Summary

所以 Event Loop (事件循環) 是什麼?

  • Event Loop (事件循環) 是 JavaScript 中的一個重要機制, 用於處理非同步操作和事件驅動的代碼,同時保持程式的反應性。
  • 通過不斷檢查 Call Stack(執行棧)和 Task Queue(任務隊列), 使 JavaScript 能夠有效處理非同步操作,而不會阻塞主執行緒,從而提供更好的使用者體驗。
  • 事件循環是實現 JavaScript 中非同步操作的核心, 確保代碼可以同時處理多個任務,使應用程序保持反應靈敏。

最後讓我們用生活上的例子來加深印象:

假設你在煮湯,同時要做兩件事 -- ① 等待鍋中的湯煮沸,② 同時切番茄。 通常你應該不會一直盯著鍋等湯煮沸,而是會這麼做:

  1. 將湯放在爐上。
  2. 開始切番茄。
  3. 檢查鍋中的湯是否煮沸。
  4. 如果湯煮沸了,就繼續做下一步,否則繼續切番茄。
  5. 重複步驟3和4,直到湯煮沸。

在這個情境下,你在等湯煮沸的同時,還能夠繼續切番茄,不需要一直等待。 這就好比 JavaScript 中的事件循環。

在 JavaScript 中,當你執行程式碼時,有時候會遇到需要等待的事情, 例如:下載網頁資料或等待用戶點擊按鈕。 為了不讓整個程式停下來等待這些事情完成,JavaScript 使用事件循環:

  1. 你告訴 JavaScript 執行某個任務,然後繼續執行下一個任務(就像切番茄一樣)。
  2. JavaScript 會檢查這個任務是否完成(就像檢查湯是否煮沸一樣)。
  3. 如果任務完成了,JavaScript 會執行相應的程式碼,然後繼續下一個任務。
  4. 這個過程不斷重複,直到所有任務都完成。

事件循環允許 JavaScript 執行非同步操作,而不會阻塞其他代碼的執行。 這就是為什麼在執行非同步操作期間,我們仍然能夠執行其他代碼的原因。

以上透過為什麼去簡單講述關於 Event Loop (事件循環)概念!希望大家都能理解~~!


參考資料

ExplainThis MDN