如上圖所示,Javascript執行引擎的主線程運行的時候,產生堆(heap)和棧(stack),程序中代碼依次進入棧中等待執行,若執行時遇到異步方法,該異步方法會被添加到用於回調的隊列(queue)中【即JavaScript執行引擎的主線程擁有一個執行棧/堆和一個任務隊列】。javascript
棧(stack) : 函數調用會造成了一個堆棧幀
堆(heap) : 對象被分配在一個堆中,一個用以表示一個內存中大的未被組織的區域。
隊列(queue) : 一個 JavaScript 運行時包含了一個待處理的消息隊列。每個消息都與一個函數相關聯。當棧爲空時,則從隊列中取出一個消息進行處理。這個處理過程包含了調用與這個消息相關聯的函數(以及於是建立了一個初始堆棧幀)。當棧再次爲空的時候,也就意味着該消息處理結束。 html
首先,咱們對圖中的一些名詞稍加解釋:java
【注:由於主線程從"任務隊列"中讀取事件的過程是循環不斷的,所以這種運行機制又稱爲Event Loop(事件循環)】web
下面咱們經過setTimeout來看看單線程的JavaScript執行引擎是如何來執行該方法的。瀏覽器
console.log(1);
setTimeout(function() {
console.log(2);
},5000);
console.log(3);
//輸出結果:
//1
//3
//2複製代碼
基本上,一個完整的事件循環模型就講完了。如今咱們來重點關注一下隊列。
異步任務分爲兩種:Macrotasks 和 Microtasks。併發
Macrotasks 和 Microtasks有什麼區別呢?咱們以setTimeout和Promises來舉例。異步
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
Promise.resolve().then(function() {
console.log('3');
}).then(function() {
console.log('4');
});
console.log('5');
//輸出結果:
//1
//5
//3
//4
//2複製代碼
緣由是Promise中的then方法的函數會被推入 microtasks 隊列,而setTimeout的任務會被推入 macrotasks 隊列。在每一次事件循環中,macrotask 只會提取一個執行,而 microtask 會一直提取,直到 microtasks 隊列清空。
結論以下:svg
【注:通常狀況下,macrotask queues 咱們會直接稱爲 task queues,只有 microtask queues 纔會特別指明。】函數
JavaScript 運行機制詳解:再談Event Loop
併發模型與Event Loop
【轉向Javascript系列】從setTimeout說事件循環模型
異步 JavaScript 之理解 macrotask 和 microtask oop