Event-Loop 詳解

time911發表於2019-04-29

JavaScript是單執行緒

JavaScript是單執行緒的,也就是說同一時間只能做一件事。 這和它執行在瀏覽器有關,作為指令碼語言,JavaScript只要使用者操作DOM以及使用者操作。 這決定了它只能是單執行緒,否則會帶來很多複雜的同步問題。

HTML5提出Web Worker標準,允許JavaScript指令碼建立多個執行緒。
但是子執行緒完全受主執行緒控制,且不得操作DOM。
所以,這個新標準並沒有改變JavaScript單執行緒的本質。
複製程式碼

任務佇列

單執行緒意味著所有的任務都要排隊,前一個任務結束了才會執行後一個任務。如果有些任務耗時過長,那麼就不得不一直等待下去。
在JavaScript中,耗時較長的操作主要是使用者操作、網路請求、IO裝置。往往這個時候CUP是處於閒置的,造成資源浪費。JavaScript設計者意識到網路請求和IO裝置等耗時操作可以暫時不管他,先執行後面的任務,等耗時的操作有了結果回撥再去之後再去執行相應操作。這樣大大的提高了資源的利用和程式碼執行速度。
因此任務分為同步任務和非同步任務。簡單來說,同步任務是在主執行緒上的任務,該任務只有在前面任務執行完畢才可以執行。非同步任務則是進入任務佇列(task queue),當得知改任務可以執行時才會進入主執行緒執行。

同步非同步執行順序如下:
(1)所有同步任務都在主執行緒上執行,形成一個執行棧

(2)主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了執行結果,就在"任務佇列"之中放置一個事件。

(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。

(4)主執行緒不斷重複上面的第三步。
複製程式碼

事件和回撥函式

任務佇列就是事件佇列。每當IO裝置完成一項任務,就在任務佇列新增一個事件。意味著相關的非同步操作可以進入執行棧。
任務佇列中的事件,除了IO裝置的事件以外,還包括一些使用者產生的事件(比如滑鼠點選、頁面滾動等等)。只要指定過回撥函式,這些事件發生時就會進入"任務佇列",等待主執行緒讀取。
回撥函式就是被主執行緒掛起的程式碼,非同步任務必須制定回撥函式,當主執行緒開始執行非同步操作,就是執行相應的回撥函式。
任務佇列是先進先出的資料結構。同時主執行緒讀取任務佇列的規則是,主執行緒執行棧空是就去讀取任務佇列。

Event Loop

下圖轉引自Philip Roberts的演講的PPT
有興趣的同學看這裡《Help, I'm stuck in an event-loop》

Event-Loop 詳解