對於event loop 能夠抽象成一段簡單的代碼表示javascript
for (macroTask of macroTaskQueue) { // 1. Handle current MACRO-TASK handleMacroTask(); // 2. Handle all MICRO-TASK for (microTask of microTaskQueue) { handleMicroTask(microTask); } }
javascript是一個單線程語言,同一時間只能執行一個任務。
對於javascript的事件處理機制,咱們能夠簡單理解成「主線程+任務隊列」模式。主要步驟以下html
(1)全部同步任務都在主線程上執行,造成一個執行棧。(2)主線程以外,還存在一個 "任務隊列"(task queue)。只要異步任務有了運行結果,就在 "任務隊列" 之中放置一個事件。
(3)一旦 "執行棧" 中的全部同步任務執行完畢,系統就會讀取 "任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。java(4)主線程不斷重複上面的第三步。web
任務隊列分爲task queue和microtask queue。執行棧任務清空後會先從Microtasks中取任務,Microtasks中執行完以後纔會執行task中的任務。segmentfault
所以一個event loop主要流程以下:api
流程圖以下:瀏覽器
再仔細理解一下開頭列出的代碼:網絡
for (macroTask of macroTaskQueue) { // 1. Handle current MACRO-TASK handleMacroTask(); // 2. Handle all MICRO-TASK for (microTask of microTaskQueue) { handleMicroTask(microTask); } }
對於VUE這類web2.0框架而言,最主要作的應該仍是把對data的修改映射到DOM上。若是隻修改一個數據,就刷新一次DOM,那麼在一個同步過程當中,同時修改好幾個數據,必然會致使屢次渲染。這確定是不可取的。
從上面的event loop咱們瞭解到,一個event loop對應一次render。理想情況固然就是,一次event loop產生的全部改動最好再render以前將DOM都先更新好。這樣在一個週期中就能夠只render一次。app
從這點需求出發很容易發現,microtask queue很符合要求。框架
實際代碼中,不論是鼠標點擊仍是鍵盤輸入或是網絡時間,觸發了哪些方法,這些觸發均可以當作開啓一個event loop。這些觸發形成的任何修改都放到microtask queue中,就能夠保證在這一輪的evnet loop走到render ui時能夠拿到最新的DOM。
要說明的是,這裏的render並非維護虛擬DOM,也不是把虛擬DOM的變化投射到真實DOM上。而是將真實DOM更新到UI的過程。
這麼說是由於:Event Loop 並非在 ECMAScript 標準中定義的,而是在 HTML 標準中定義的:
To coordinate events, user interaction, scripts, rendering, networking, and so forth...
在 JavaScript Engine 中(以 V8 爲例),只是實現了 ECMAScript 標準,而並不關心什麼 Event Loop。也就是說 Event Loop 是屬於 JavaScript Runtime 的,是由宿主環境提供的(好比瀏覽器)。
瀏覽器可不會關心什麼虛擬DOM。只負責DOM改變後渲染UI。
同上: 在開啓一個event loop後,若是將任務放到task queue中,那麼這個task任務只會在本輪Event loop結束後纔會執行,並開啓新一輪event loop。這無疑會致使兩次render UI。
實際上,尤大爲了修復一些bug,曾經將VUE.nexttick用task queue實現。可是致使了很明顯的性能問題。
能夠看看兩個列子: 例一 , 例二
兩個fiddle的實現如出一轍,就是讓那個絕對定位的黃色元素起到一個fixed定位的效果:綁定scroll事件,每次滾動的時候,計算當前滾動的位置並更改到那個絕對定位元素的top屬性上去。你們本身試試滾動幾下,對比下效果,你就會發現第一個fiddle中的黃元素是穩定不動的,fixed很好。然後一個fiddle中就有問題了,黃色元素上下晃動,彷佛跟不上咱們scroll的節奏,總要慢一點,雖然最後停下滾動時位置是對的。
上述兩個例子實際上是在這個issue中找到的,第一個jsfiddle使用的版本是Vue 2.0.0-rc.6,這個版本的nextTick實現是採用了MO,然後由於IOS9.3的WebView裏的MO有bug,因而尤雨溪更改了實現,換成了window.postMessage,也就是後一個fiddle所使用的Vue 2.0.0-rc.7。後來尤雨溪瞭解到window.postMessage是將回調放入的macrotask 隊列。這就是問題的根源了。