JavaScript 的執行緒
作為瀏覽器語言,JavaScript 會對 DOM 進行操作,如果存在多個執行緒同時操作 DOM,一個執行緒建立了一個節點,另外一個執行緒去刪除這個節點,瀏覽器渲染就會亂套。所以,JavaScript 被設計成單執行緒。
單執行緒指的是 JavaScript 引擎解析和執行程式碼的執行緒只有一個,這個執行緒為主執行緒,所有的同步任務都在主執行緒上執行。
同步和非同步
單執行緒就意味著所有的任務都是同步的,下一個任務必須在上一個任務完成後再執行。假如有一個同步的網路請求,我們等待的時間是不確定的,在請求響應前,瀏覽器就會處於阻塞狀態。
所以 JavaScript 提供了非同步任務,我們不需要關心非同步任務何時響應,這樣就可以保證主執行緒的任務暢通無阻的執行下去。
任務佇列
非同步任務不進入主執行緒,而是進入任務佇列,當非同步任務完成,任務佇列通知主執行緒,該任務才會進入主執行緒。佇列的特點是先進先出,任務佇列中的事件會之按照放入的時間先後順序執行。
Event Loop
由於主執行緒讀取任務佇列的的過程是迴圈不斷的,所以這種機制被稱為 Event Loop(事件迴圈)。
- 所有同步任務都在主執行緒(stack)上執行,形成一個執行棧。
- 主執行緒之外,還存在一個任務佇列(callback queue),只要非同步任務有了執行結果,就在任務佇列之中放置一個事件。
- 一旦執行棧中的所有同步任務執行完畢,系統就會讀取任務佇列,將佇列中的事件放到執行棧中依次執行。
- 主執行緒從任務佇列中讀取事件,這個過程是迴圈不斷的。
巨集任務和微任務
任務佇列可分為巨集任務佇列和微任務佇列
- 巨集任務:setTimeout、setInterval、setImmediate、I/O 等。
- 微任務:then、process.nextTick、Object.observer 等。
在瀏覽器環境中,當執行棧的任務執行完畢,先去讀取微任務佇列,微任務執行完畢,再從巨集任務佇列讀取一個事件放到執行棧去執行,執行完畢後再去取微任務佇列,如此重複。
在 Node 環境中,Event Loop 分為 6 個階段,每一個階段對應著一個巨集任務佇列。
與瀏覽器環境不同,Node 環境下,當執行棧的任務執行完畢後,先清空微任務佇列。再取到巨集任務佇列所有事件去執行,執行完畢轉到下一個階段,此時再去清空微任務佇列。