今天,我想來點 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 (事件循環) 有以下重要觀念:
Heap (堆): Heap (堆) 用於存儲複雜的數據。堆的特點是其大小比較靈活,可以根據需要動態分配和釋放內存。通常用於存儲動態生成的數據,例如用戶輸入或在運行時創建的對象。
Stack (棧): Stack (棧) 特點是
後進先出(Last-In, First-Out,LIFO)
的原則。當一個函數被調用時,其局部變數和函數調用信息被壓入棧的頂部。當函數執行完成後,這些信息被彈出,直到棧被清空。棧主要用於跟蹤函數調用和局部變數。Queue (隊列): Queue (隊列) 特點是
先進先出(First-In, First-Out,FIFO)
的原則。在 JavaScript 的執行環境中,等待處理的任務通常會被放在隊列中,例如事件處理程序、非同步操作的回調函數等。最早進入隊列的任務會首先被處理。
Event Loop (事件循環) 的工作方式:
檢查 Call Stack(執行棧):
事件循環首先檢查執行棧是否為空。
執行棧是一個紀錄當前執行上下文的結構,如果它為空,表示目前沒有待處理的同步代碼。檢查 Task Queue(任務隊列):
如果執行棧為空,事件循環接下來會檢查任務隊列。
任務隊列是用於存儲待處理的非同步操作的佇列,通常包括定時器、事件處理程序回調等。將下一個任務的回調函數推入執行棧:
如果執行棧和任務隊列都不為空,事件循環將從隊列中取出下一個任務的回調函數,並將它推入執行棧中開始執行。
這表示開始處理一個非同步操作。執行回調函數:
執行棧中的回調函數開始執行,處理相應的任務或事件。
這可以是網絡請求的回調、定時器的回調、按鈕點擊事件的回調等等。檢查執行棧和任務隊列:
回調函數執行完成,事件循環會再次檢查執行棧和任務隊列。
如果執行棧為空,且隊列中還有待處理的任務,則重複上述步驟,
將下一個任務的回調函數推入執行棧,繼續處理其他非同步操作。重複運行:
事件循環將以上步驟不斷重複,確保 JavaScript 能夠同時處理多個任務和事件,從而保持程式的反應性。
Brief Summary
所以 Event Loop (事件循環) 是什麼?
- Event Loop (事件循環) 是 JavaScript 中的一個重要機制,
用於處理非同步操作和事件驅動的代碼,同時保持程式的反應性。 - 通過不斷檢查 Call Stack(執行棧)和 Task Queue(任務隊列),
使 JavaScript 能夠有效處理非同步操作,而不會阻塞主執行緒,從而提供更好的使用者體驗。 - 事件循環是實現 JavaScript 中非同步操作的核心,
確保代碼可以同時處理多個任務,使應用程序保持反應靈敏。
最後讓我們用生活上的例子來加深印象:
假設你在煮湯,同時要做兩件事 – ① 等待鍋中的湯煮沸,② 同時切番茄。
通常你應該不會一直盯著鍋等湯煮沸,而是會這麼做:
- 將湯放在爐上。
- 開始切番茄。
- 檢查鍋中的湯是否煮沸。
- 如果湯煮沸了,就繼續做下一步,否則繼續切番茄。
- 重複步驟3和4,直到湯煮沸。
在這個情境下,你在等湯煮沸的同時,還能夠繼續切番茄,不需要一直等待。
這就好比 JavaScript 中的事件循環。
在 JavaScript 中,當你執行程式碼時,有時候會遇到需要等待的事情,
例如:下載網頁資料或等待用戶點擊按鈕。
為了不讓整個程式停下來等待這些事情完成,JavaScript 使用事件循環:
- 你告訴 JavaScript 執行某個任務,然後繼續執行下一個任務(就像切番茄一樣)。
- JavaScript 會檢查這個任務是否完成(就像檢查湯是否煮沸一樣)。
- 如果任務完成了,JavaScript 會執行相應的程式碼,然後繼續下一個任務。
- 這個過程不斷重複,直到所有任務都完成。
事件循環允許 JavaScript 執行非同步操作,而不會阻塞其他代碼的執行。
這就是為什麼在執行非同步操作期間,我們仍然能夠執行其他代碼的原因。
以上透過為什麼去簡單講述關於 Event Loop (事件循環)概念!希望大家都能理解~~!
參考資料:
➫ ExplainThis
➫ MDN