再看瀏覽器事件循環和NodeJS事件循環

事件循環是瀏覽器和Node用來解決JS單線程運行帶來的問題的一種運行機制。瀏覽器和NodeJS環境下的事件循環是不同的,瀏覽器是完全遵循HTML5規範去實現的,NodeJS的事件循環是基於libuv實現的,在HTML5規範的基礎上做了些取捨,爲了保證上層(即javascript)語法一致,API方面和瀏覽器差不多,但是宏/微任務的執行邏輯和瀏覽器完全不一樣。比如NodeJS新增了nextTick、setImmediate,而沒有MutationObserver。
瀏覽器和NodeJS下宏任務和微任務分別有:

可以看到:
1、瀏覽器端只有IE纔有setImmediate,NodeJS端又啓用了它。
2、MutationObserver用來觀察Dom節點變化,包括子節點的刪除、屬性修改、文本內容修改等等。
3、requestAnimationFrame(foo)涵義是延遲執行foo,而延遲多長時間由瀏覽器根據操作系統當前的顯示器刷新率來決定,從而保證它的執行和顯示器刷新頻率保持一致,避免丟幀現象。解決了用setTimeout製作動畫時自行設定延時不準確而丟幀的問題,所以requestAnimationFrame不是setTimeout的替代,而是特定場景下的補充。需要注意的是這個方法雖然能夠保證回調函數在每一幀內只渲染一次,但是如果這一幀有太多任務執行,還是會造成卡頓的;因此它只能保證重新渲染的時間間隔最短是屏幕的刷新時間。
4、我不認爲requestAnimationFrame和UI rendering屬於宏任務,所以劃掉了。
怎麼看UI render呢?
網上不少地方說UI render(也就是繪製界面)也列爲宏任務,但我從HTML標準裏看到,其實UI render和宏任務是平行的,完整的事件循環是:執行第一個宏任務 -> 執行所有微任務 -> UI render -> 執行下一個宏任務...。重點看UI render,HTML標準裏提供了一段僞代碼,用來說明UI render階段幹了什麼:可以看到UI render內部先後處理了窗口變化、滾動、媒體查詢、CSS動畫、全屏、requestAnimationFram的回調、IntersectionObserver的回調,最後再繪製。也就是說這些異步任務都不屬於宏任務和微任務的範疇,而是集中在UI render階段執行。
1、我們看一看IntersectionObserver,它是用來監聽元素是否出現(出現的比例)在屏幕視口的。
2、我們再看一個也會產生異步任務的API,requestIdleCallback。簡單來說,它是讓回調函數在進程空閒時再執行,如果進程忙的話就延後到下一次。可以傳一個timeout時間requestIdleCallback(foo, {timeout:6000}),表示6000ms以後就算進程忙也要強制執行。那麼怎麼纔算進程不忙呢?HTML標準做了說明:當宏任務和微任務全部執行完了纔算。另外也可以看出,requestIdleCallback的回調是在UI render的最後一步(第12步)才執行的,而且還有可能不執行,優先級非常之低。3、除了上面講的窗口變化、滾動、全屏等dom事件,其他dom事件基本都是同步的,不是異步任務。

NodeJS的事件循環

NodeJS 採用 V8 作爲JS解析引擎,而I/O處理方面使用了自己設計的libuv,libuv是一個基於事件驅動的跨平臺抽象層,封裝了不同操作系統一些底層特性,對外提供統一的API,事件循環機制也是它裏面的實現。在瀏覽器中,可以認爲只有一個宏隊列,所有的macrotask都會被加到這一個宏隊列中,但是在NodeJS中,不同的macrotask會被放置在不同的宏隊列中,有
1、Timers Queue(放置setTimeout、setInterval的回調)
2、IO Callbacks Queue
3、Check Queue(放置setImmediate的回調)
4、Close Callbacks Queue
在瀏覽器中,也可以認爲只有一個微隊列,所有的microtask都會被加到這一個微隊列中,但是在NodeJS中,不同的microtask會被放置在不同的微隊列中,所以執行微任務的時候,NodeJS會優先執行NextTick Queue裏的所有任務,再執行Other Micro Queue的所有任務。
1、NextTick Queue(放置process.nextTick的回調)
2、Other Micro Queue(放置Promise等其他microtask)
所以,NodeJS環境的事件循環是:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章