轉自:https://mp.weixin.qq.com/s/3pX-qNO_dC6ijG3bXKzROgvue
event loop 即事件循環。最初瞭解到js的event loop機制是經過本身對js中異步、同步的疑惑。今天聊一聊本身的理解,但願和你們一塊兒學習。web
首先,讓咱們看一個經典的setTimeOut的問題ajax
console.log(1)
setTimeOut(function(){
console.log(2)
},1000)
setTimeOut(function(){
console.log(3)
},0)
console.log(4)
複製代碼
瀏覽器打印的結果是怎樣的呢?你們能夠寫一段腳本試一下,打印的結果是1,4,3,2;爲何不是按照js從上到下的執行順序,輸出1,3,4,2呢?這就要說到咱們今天的主題,js的事件循環機制了。瀏覽器
想要了解event loop咱們就要從js的工做原理提及。首先,你們都知道js是單線程的。所謂單線程就是進程中只有一個線程在運行。那麼,js爲何是單線程而不是作成多線程的呢?我的理解,js是用來實現瀏覽器與用戶之間的交互的。若是同時要處理用戶點擊,用戶輸入,用戶關閉等操做,瀏覽器沒法知道這個時間我到底應該作什麼。因此js是從上至下按順序運行下去的。這裏談及到兩個名詞,線程和進程。簡單介紹:進程,能夠理解爲正在運行的程序的實體。例如,在手機中打開一個app,打開了一個後臺進程。那麼,線程又是什麼呢?線程是程序執行流的最小單位,也叫輕量級的進程。是程序執行的過程當中,一個單一的順序控制流程。一個進程中,包含不少的線程。單線程,程序執行的過程當中,所走的程序路徑按照連續的順序排下來。前面的必須處理好,後面的纔會執行。多線程
按照單線程的思想,順序執行咱們的代碼,那麼,若是咱們的js中間向後臺發送一個ajax請求,就要等到請求等到結果後纔會繼續向下執行。若是請求耗時10秒,頁面就要停在這裏10秒。這樣的用戶體驗很很差。。。所以,就有了同步任務、異步任務的區別。所謂異步任務,就比如咱們在燒水的同時看書,等到水燒好了,再用燒好的水煮麪。這就是一個簡單的異步操做。異步能夠提升處理事件的效率。異步任務就能夠解決單線程按照順序依次執行,不能夠同時進行多個任務的問題。app
同步任務和異步任務在js中是如何執行的呢?js的代碼運行會造成一個主線程和一個任務隊列。主線程會從上到下一步步執行咱們的js代碼,造成一個執行棧。同步任務就會被放到這個執行棧中依次執行。而異步任務被放入到任務隊列中執行,執行完就會在任務隊列中打一個標記,造成一個對應的事件。當執行棧中的任務所有運行完畢,js會去提取並執行任務隊列中的事件。這個過程是循環進行的,這就是咱們今天想要了解的event loop。異步
這裏簡單粗暴的理解一下異步任務,什麼樣的任務會被放到任務隊列中呢?簡單理解,有callback函數的就能夠被看作是異步任務,會被放到任務隊列中執行。你們可能在使用vue的時候用到過nextTick——>任務隊列。函數
咱們回過頭再來看一下最開始提到的問題。console.log(1)和console.log(4)在主線程的執行棧中執行完,此時,執行棧被清空,js開始執行任務隊列中的兩個setTimeOut事件。先執行延遲時間設置爲0秒的setTimeOut打印出3,再執行1秒的setTimeOut事件,打印出2。最後的輸出結果就是一、四、三、oop
由於js的event loop機制,因此你們不要認爲setTimeOut設置的事件到了延遲時間就是被執行。若是你的執行棧任務沒有被所有執行完,清空。setTimeOut事件執行的時間頗有多是要大於你設置的延時參數。性能
經過了解js的實現基礎和它的執行順序,進一步讓我理解裏eventloop的工做原理。腦子裏有了一個執行機制的大概流程。經過開頭的setTimeout引出了事件循環的概念,隨着ES6的普遍應用。一樣解決異步問題的Promise對象,能夠經過它的鏈式寫法,達到寫同步代碼的手法實現異步任務的效果。那麼,Promise和setTimeout在事件隊列裏是否同樣呢?
console.log(1);
setTimeout(function(){
console.log(2);
},0);
Promise.resolve().then(function(){
console.log(3);
}).then(function(){
console.log(4);
});
console.log(5);
複製代碼
這段js執行的結果是1,5,2,3,4麼?你們能夠嘗試一下。根據上面獲得的結論,首先輸出的應該是1,5,由於console.log(1)和console.log(5)是執行棧裏的同步任務,完成後才進行事件循環。那麼setTimeout和Promise的執行順序是怎樣的呢?
這裏就要引入兩個新名詞,microtask、macrotask。即宏任務和微任務。
宏任務: 須要屢次事件循環才能執行完,事件隊列中的每個事件都是一個宏任務。瀏覽器爲了可以使得js內部宏任務與DOM任務有序的執行,會在一個宏任務執行結束後,在下一個宏執行開始前,對頁面進行從新渲染 (task->渲染->task->…)鼠標點擊會觸發一個事件回調,須要執行一個宏任務,而後解析HTML。setTimeout的做用是等待給定的時間後爲它的回調產生一個新的宏任務。
微任務: 微任務是一次性執行完的。微任務一般來講是須要在當前task執行結束後當即執行的任務,例如對一些動做作出反饋或者異步執行任務又不須要分配一個新的task,這樣即可以提升一些性能。只要執行棧中沒有其餘的js代碼正在執行了,並且每一個宏任務都執行完了,微任務隊列會當即執行。若是在微任務執行期間微任務隊列加入了新的微任務,會將新的微任務加入隊列尾部,以後也會被執行。簡單理解,宏任務在下一輪事件循環執行,微任務在本輪事件循環的全部任務結束後執行。
宏任務主要包括了:setTimeout、setInterval、setImmediate、I/O、各類事件(好比鼠標單擊事件)的回調函數
優先級:主代碼塊 > setImmediate > MessageChannel > setTimeout / setInterval
微任務主要包括了:process.nextTick、Promise、MutationObserver
優先級:process.nextTick > Promise > MutationObserver
看到這裏,想必你們已經獲得了上面代碼塊的執行結果。對於事件循環機制粗淺的介紹到這裏,但願能幫助到你們。若是文中哪裏闡述有問題,還請各位大神多多指點。