1. 程式人生 > >javascript事件迴圈機制

javascript事件迴圈機制

理解Event-Loop

Event Loop(事件輪詢)機制是一個經常把人搞暈的東東。我不敢說我完全明白,只是在此談談我的淺見。

事件的處理

瀏覽器是一個事件驅動(event-driven)架構的軟體。它的UI執行緒中會不斷產生使用者事件。但是處理事件的JavaScript是單執行緒執行的,這是一個瀏覽器環境下難以改變的現狀(HTML5 Web Works沒有從本質上改變這個模型)。這意味著:在JavaScript處理某個任務(執行某段程式碼)過程中,如果產生了使用者事件,它不會立即被處理。那這種情況該怎麼辦呢?

瀏覽器維護了一個“任務佇列”(一個優先佇列資料結構),它是一個瀏覽器程序資源。每當UI執行緒產生一個事件,事件物件就被當做任務放入任務佇列中(enqueue)。當JavaScript執行執行緒空閒的時候,佇列中的一個任務就會被送往JavaScript執行執行緒(dequeue),進行相應的處理。
這個enqueue和dequeue的機制就是“Event Loop”。
但是,不僅使用者事件可以被Event Loop機制處理,還能更多的東西是依賴這個機制的。

非同步IO的處理

如果沒有非同步的理念,這個世界會完全不同:一個耗時的I/O操作(例如HTTP請求)會導致JavaScript執行執行緒等待,而後續的操作得不到執行。這種情況下,一個耗時的伺服器端資料庫操作http請求,會讓JavaScript執行執行緒阻塞,瀏覽器將長期處於假死狀態,在此期間,其他後續操作(包括使用者的互動事件)得不到響應。

好在瀏覽器不是單執行緒的。它可以(但不是必須)讓這些I/O任務讓其他執行緒來託管,這樣就形成了一個執行任務的執行緒池。但是這些任務的結果總歸要回到JavaScript執行執行緒上處理,於是這些任務也被放到任務佇列中:需要被託管的任務被放入佇列中(enqueue),已完成的任務會被從佇列中一個個取出(dequeue),回到JavaScript執行執行緒執行回撥。在這些耗時的I/O任務被託管的時候,JavaScript執行執行緒可以執行其他程式碼。

在Node中,這個過程是類似的。本文不表。
這便是非同步的原理了。我們看到它同樣依賴Event Loop的機制。

定時器

瀏覽器的全域性物件window提供了兩個方法,setTimeout和setInterval。這兩個方法其實是呼叫了瀏覽器的API,將一個任務移除出JavaScript執行執行緒中,延時處理。

我們現在馬上可以反應過來:這個將要被延時的任務同樣是放到了任務佇列中。在一次Event Loop過程中,它會優先將該時間點下已經到時的延時任務移除出佇列,放入JavaScript執行執行緒中。這意味著,任務佇列是一個優先佇列。

但是由於JavaScript執行執行緒的執行時間是不確定的,所以這個延時只是一個大體的值,它取決於JavaScript執行執行緒的執行時間。

回撥函式

任務完成的時候,JavaScript需要執行哪段程式碼來處理呢?當然是回撥函數了。

但是不免奇怪的一點就是:JavaScript中怎麼知道要執行的是哪個回撥函式呢?答案就是:任務被放入任務佇列的時候,該任務的回撥函式會被註冊(註冊到什麼地方?需要進一步探究)。這樣,當特定任務完成的時候,任務結果和回撥標記會返回給JavaScript執行執行緒,進入執行棧。

事件處理器

與其他任務不同,事件並不是由JavaScript執行執行緒發出的,而是從UI執行緒中發出的。
事件處理器和回撥函式類似。但是特定的事件處理器在瀏覽器進入非同步事件驅動階段時就會針對特定的事件註冊。當事件物件返回到JavaScript執行執行緒時,事件處理器也會同時進入執行棧中執行。

結束

越往後寫,越發現我之前的一些理解有偏差。在學習過程中,我也要多反思,多總結。之前寫的不對的地方,我也會盡早糾正。