概念
js是基於單執行緒執行的,而一些特定事件又是非同步執行的,所以這種單執行緒+非同步的執行方式一定是事件驅動的 而一般瀏覽器環境下有這樣幾種執行緒。
js引擎執行緒 (解釋執行js程式碼、使用者輸入、網路請求)主執行緒
GUI執行緒 (繪製使用者介面、與js主執行緒是互斥的)先繪製dom再繪製css
http網路請求執行緒 (處理使用者的get、post等請求,等返回結果後將回撥函式推入任務佇列)
定時觸發器執行緒 (setTimeout、setInterval等待時間結束後把執行函式推入任務佇列中)
瀏覽器事件處理執行緒(將click、mouse等互動事件發生後將這些事件放入事件佇列中)
上一張經典的eventLoop圖,瞭解幾個基本概念
1,棧(stack):佇列優先,由作業系統自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。2.堆(heap):先進後出;動態分配的空間
JavaScript中的變數分為基本型別和引用型別。基本型別就是儲存在棧記憶體中的簡單資料段,而引用型別指的是那些儲存在堆記憶體中的物件
這裡引入一個概念(圖上沒有),就是js中的task和microTask 一般來講,我們把setTimeout,setInterval,瀏覽器的dom操作,使用者點選等互動事件歸結為task,而Promise callback Mutation callback還有nextTick我們歸結為microTask,
那問題就來了,js中task和microTask執行順序是怎樣的
由於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