js是基於單線程運行的,而一些特定事件又是異步執行的,因此這種單線程+異步的執行方式必定是事件驅動的 而通常瀏覽器環境下有這樣幾種線程。css
js引擎線程 (解釋執行js代碼、用戶輸入、網絡請求)主線程promise
GUI線程 (繪製用戶界面、與js主線程是互斥的)先繪製dom再繪製css瀏覽器
http網絡請求線程 (處理用戶的get、post等請求,等返回結果後將回調函數推入任務隊列)bash
定時觸發器線程 (setTimeout、setInterval等待時間結束後把執行函數推入任務隊列中)網絡
瀏覽器事件處理線程(將click、mouse等交互事件發生後將這些事件放入事件隊列中)數據結構
上一張經典的eventLoop圖,瞭解幾個基本概念多線程
1,棧(stack):隊列優先,由操做系統自動分配釋放 ,存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。那問題就來了,js中task和microTask執行順序是怎樣的dom
因爲js是單線程,全部的多線程任務最後都要壓入主線程執行棧去執行,如圖所示,task會放入回調隊列裏 由eventLoop取出依次執行。異步
這裏能夠解釋一個小問題,若是多個setTimeout的狀況下,執行時間不是特別的精確,setTimeout 它會在延遲時間結束後分配一個新的 task 至 event loop 中,而不是當即執行,因此 setTimeout 的回調函數會等待前面的 task 都執行結束後再運行。函數
說到這裏好像尚未microTask什麼事,那microTask在何時執行呢,通俗的說,你能夠將microTask理解爲一個愛插隊的大媽,就是在一個task事件結束後,microTask就插入執行棧當即執行,執行順序是主線程執行棧的隊尾插入,callback quene以前
上一段代碼吧,來直觀的瞭解一下執行順序
console.log('start');
setTimeout(function() {
console.log('setTimeout1');
setTimeout(function() {
console.log('setTimeout2');
},0);
console.log('setTimeout3');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('end');
複製代碼
最後結果是 1,start 2,end 3,promise1 4,promise2 5,setTimeout1,6,setTimeout3 6,setTimeout2
順序是,先執行打印start 而後打印edn 這時候microTask (promise)插隊進入 打印promise1,promise2, 而後執行setTimeout打印setTimeout1,setTimeout3,這裏能夠看作
console.log('setTimeout1');
console.log('setTimeout3');
這兩句一塊兒壓入執行棧,
setTimeout(function() {
console.log('setTimeout2');
},0);
複製代碼
進入task隊列,由EventLoop取出,因此最後執行順序是 1,start 2,end 3,promise1 4,promise2 5,setTimeout1,6,setTimeout3 6,setTimeout2