直接上題,答對解釋通算你贏,就不用看解析了。前端
點擊頁面後,下面代碼的輸出結果是什麼?面試
document.addEventListener('click', function(){
Promise.resolve().then(()=> console.log(1));
console.log(2);
})
document.addEventListener('click', function(){
Promise.resolve().then(()=> console.log(3));
console.log(4);
})
複製代碼
輸出結果ajax
2, 1, 4, 3後端
JS異步執行原理: js執行引擎只有一個主線程執行代碼邏輯,遇到須要異步執行的任務代碼,會將其添加事件隊列中。當主線程空閒時,輪詢事件隊列中能夠執行的任務,將其放到主線程進行執行,以此類推,直到事件隊列中無可執行的任務。以下圖所示:promise
JS引擎只是執行事件隊列中的異步代碼,但事件隊列中的信息來源並非JS引擎,而是由瀏覽器中的其餘相關線程產生的,以下圖所示:瀏覽器
以 http 傳輸線程爲例: 最多見的就是 js 代碼發出 ajax 請求,而後就是交給瀏覽器的http線程去處理了,當後端有數據返回時,http 線程在事件隊列中生成一個數據已ready好的事件,等待 JS 主線程空閒時執行。bash
再好比,咱們常見的click,mouse事件,都是GUI 事件觸發線程生成的。當用戶點擊頁面時,GUI 事件觸發線程就會在事件隊列中生成一個click事件,等待 JS 主線程空閒時執行。異步
瀏覽器中的事件循環的任務隊列被劃分爲宏任務和微任務兩種類型:函數
macrotask:包含執行總體的js代碼
script
,事件回調,XHR回調,定時器(setTimeout
、setInterval
、setImmediate
),IO操做,UI renderoop
microtask:更新應用程序狀態的任務,包括promise回調,
MutationObserver
,process.nextTick
,Object.observe
mactotask & microtask的執行順序以下圖所示:
總結起來,一次事件循環的步驟包括:
回顧上面的事件循環示意圖,update rendering(視圖渲染)發生在本輪事件循環的microtask隊列被執行完以後,也就是說執行任務的耗時會影響視圖渲染的時機。一般瀏覽器以每秒60幀(60fps)的速率刷新頁面,聽說這個幀率最適合人眼交互,大概16.7ms渲染一幀,因此若是要讓用戶以爲順暢,單個macrotask及它相關的全部microtask最好能在16.7ms內完成。
但也不是每輪事件循環都會執行視圖更新,瀏覽器有本身的優化策略,例如把幾回的視圖更新累積到一塊兒重繪,重繪以前會通知requestAnimationFrame執行回調函數,也就是說requestAnimationFrame回調的執行時機是在一次或屢次事件循環的UI render階段。
示例以下:
setTimeout(function() {console.log('timer1')}, 0)
requestAnimationFrame(function(){
console.log('UI update')
})
setTimeout(function() {console.log('timer2')}, 0)
new Promise(function executor(resolve) {
console.log('promise 1')
resolve()
console.log('promise 2')
}).then(function() {
console.log('promise then')
})
console.log('end')
複製代碼
可能輸出結果:
promise 1, promise 2, end, promise then, timer1, timer2, UI update
promise 1, promise 2, end, promise then, UI update, timer1, timer2
瀏覽器環境下,microtask 的任務隊列是每一個 macrotask 執行完以後執行。而在 Node.js 中,microtask 會在事件循環的各個階段之間執行,也就是一個階段執行完畢,就會去執行 microtask 隊列的任務。
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
複製代碼
瀏覽器端運行結果:timer1=>promise1=>timer2=>promise2
Node 端運行結果:timer1=>timer2=>promise1=>promise2
瀏覽器和 Node 環境下,microtask 任務隊列的執行時機不一樣
Node 端,microtask 在事件循環的各個階段之間執行
瀏覽器端,microtask 在事件循環的 macrotask 執行完以後執行
具體可參考:blog.csdn.net/Fundebug/ar…
掃一掃 關注個人公衆號【前端名獅】,更多精彩內容陪伴你!