JS開發者應懂的33個概念系列1&&9--調用堆棧 && 消息隊列和事件循環

JS分爲同步任務和異步任務,好比:ajax

// 同步任務
console.log('start');
function first() {
    console.log('first');
    second();
}
function second() {
    console.log('second');
}
first();

// 異步任務
setTimeout(function() {
  console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
  console.log('promise1');
})
process.nextTick(function() {
    console.log('nextTick');
})
複製代碼

執行棧是什麼?

同步任務都在主線程上執行,造成一個執行棧promise

以上同步任務的執行過程圖解: 瀏覽器

消息隊列是什麼?

主線程以外,事件觸發線程管理着一個消息隊列,只要異步任務有了運行結果,就在任務隊列之中放置一個事件。
一旦執行棧中的全部同步任務執行完畢(此時JS引擎空閒),系統就會讀取消息隊列,將可運行的異步任務添加到可執行棧中,開始執行。bash

小結1:

Promise.resolve().then(function() {
  console.log('promise1');
})
console.log('end');
複製代碼

  • promise進入異步線程,執行任務,註冊回調函數then;
  • 主線程執行同步任務:console.log('end');
  • 主線程代碼執行完畢,執行回調函數:console.log('promise1');
  • 結束,輸出順序:end, promise1

宏任務(task)是什麼?

瀏覽器爲了可以使得JS內部task與DOM任務可以有序的執行,會在一個task執行結束後,在下一個 task執行開始前,對頁面進行從新渲染(task->渲染->task->...)
屬於宏任務的有:setTimeout,setInterval等與timer有關的異步

微任務(Microtask)是什麼?

微任務是一個任務隊列,這個隊列的執行順序是在清空執行棧以後。
屬於微任務的有Promise,process.nextTick,ajax等
process.nextTick的概念和then不太同樣,process.nextTick是加入到執行棧底部,因此和其餘的表現並不一致,屬於執行棧的一部分函數

重點來了:事件循環Event Loop是如何運行的?

從Event Loop談JS的運行機制

運行機制:

  • 執行一個宏任務(棧中沒有就從事件隊列中獲取)
  • 執行過程當中若是遇到微任務,就將它添加到微任務的任務隊列中
  • 同步任務執行完畢後,當即執行當前微任務隊列中的全部微任務(依次執行)
  • 當前主線程任務執行完畢,開始檢查渲染,而後GUI線程接管渲染
  • 渲染完畢後,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)

小結2:

setTimeout(function() {
    console.log('setTimeout');
})
new Promise(function(resolve) {
    console.log('promise');
    resolve();
}).then(function() {
    console.log('then');
})
console.log('console');

複製代碼

  • 先遇到setTimeout,那麼將其回調函數註冊後分發到宏任務Event Queue
  • 接下來遇到了Promise,new Promise當即執行console.log('promise');
  • then函數分發到微任務的消息隊列中
  • 遇到console.log('console'),當即執行
  • then函數拿到主線程執行console.log('then')
  • 至此全部主線程上的同步任務和微任務完成,第一輪循環結束
  • 第二輪循環以以前放進去的宏任務setTimeout的回調函數開始,輸出setTimeout
  • 第二輪循環結束,最終輸出:promise, console, then, setTimeout

到此就結束了。oop

貼上一個很是經典的例子,檢驗一下本身吧!

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
複製代碼
相關文章
相關標籤/搜索