JavaScipt 中的微任務(wù)和宏任務(wù)的理解

js 是單線程執(zhí)行的,js中的任務(wù)按順序一個一個的執(zhí)行,但是一個任務(wù)耗時太長,那么后面的任務(wù)就需要等待,為了解決這種情況,將任務(wù)分為了同步任務(wù)和異步任務(wù),而異步任務(wù)又可以分為微任務(wù)和宏任務(wù)。

首先第一段示例代碼

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

打印順序為:

script start
script end
promise1
promise
setTimeout

具體為什么會打印出這個順序,下面在具體解釋。
我們具體看一下js的執(zhí)行流程:


張倩qianniuerlv-2 JS事件循環(huán)機制(event loop)之宏任務(wù)/微任務(wù)解讀:

  1. 同步和異步任務(wù)分別進入不同的執(zhí)行"場所",同步的進入主線程,異步的進入Event Table并注冊函數(shù)
  2. 當指定的事情完成時,Event Table會將這個函數(shù)移入Event Queue。
  3. 主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會去Event Queue讀取對應(yīng)的函數(shù),進入主線程執(zhí)行。
  4. 上述過程會不斷重復(fù),也就是常說的Event Loop(事件循環(huán))。

在js引擎中,存在一個叫monitoring process的進程,這個進程會不斷的檢查主線程的執(zhí)行情況,一旦為空,就會去Event Quene檢查有哪些待執(zhí)行的函數(shù)。

微任務(wù) 和 宏任務(wù)

微任務(wù)和宏任務(wù)的問題應(yīng)該是前端面試中比較常見的,他們都從屬于異步任務(wù),主要區(qū)別在于他們的執(zhí)行順序,Event Loop的走向和取值

這張圖的意思就是:

  1. 存在微任務(wù)的話,那么就執(zhí)行所有的微任務(wù)
  2. 微任務(wù)都執(zhí)行完之后,執(zhí)行第一個宏任務(wù),
  3. 循環(huán) 1, 2

從參考博主的博客里看到這段,這邊不得不提一句,我也是看了這為博主的博客才理清楚了微任務(wù)和宏任務(wù)的概念。博主的鏈接會在文章末給出。

一個掘金的老哥(ssssyoki)的文章摘要:
那么如此看來我給的答案還是對的。但是js異步有一個機制,就是遇到宏任務(wù),先執(zhí)行宏任務(wù),將宏任務(wù)放入eventqueue,然后在執(zhí)行微任務(wù),將微任務(wù)放入eventqueue最騷的是,這兩個queue不是一個queue。當你往外拿的時候先從微任務(wù)里拿這個回掉函數(shù),然后再從宏任務(wù)的queue上拿宏任務(wù)的回掉函數(shù)。 我當時看到這我就服了還有這種騷操作。

這邊我們可以看出,微任務(wù)和宏任務(wù)是同屬于兩個不同的隊列的!?。?/p>

  • 宏任務(wù)一般包括:整體代碼script,setTimeout,setInterval、setImmediate。
  • 微任務(wù)一般包括:原生Promise(有些實現(xiàn)的promise將then方法放到了宏任務(wù)中)、process.nextTick、Object.observe(已廢棄)、 MutationObserver

一段喪心病狂的代碼

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')
    })
})

第一輪循環(huán):

  • 首先打印 1
  • 下面是setTimeout是異步任務(wù)且是宏任務(wù),加入宏任務(wù)暫且記為 setTimeout1
  • 下面是 process 微任務(wù) 加入微任務(wù)隊列 記為 process1
  • 下面是 new Promise 里面直接 resolve(7) 所以打印 7 后面的then是微任務(wù) 記為 then1
  • setTimeout 宏任務(wù) 記為 setTimeout2

第一輪循環(huán)打印出的是 1 7
當前宏任務(wù)隊列:setTimeout1, setTimeout2
當前微任務(wù)隊列:process1, then1,

第二輪循環(huán):

  • 執(zhí)行所有微任務(wù)
  • 執(zhí)行process1,打印出 6
  • 執(zhí)行then1 打印出8
  • 微任務(wù)都執(zhí)行結(jié)束了,開始執(zhí)行第一個宏任務(wù)
  • 執(zhí)行 setTimeout1 也就是 第 3 - 14 行
  • 首先打印出 2
  • 遇到 process 微任務(wù) 記為 process2
  • new Promise中resolve 打印出 4
  • then 微任務(wù) 記為 then2

第二輪循環(huán)結(jié)束,當前打印出來的是 1 7 6 8 2 4
當前宏任務(wù)隊列:setTimeout2
當前微任務(wù)隊列:process2, then2

第三輪循環(huán):

  • 執(zhí)行所有的微任務(wù)
  • 執(zhí)行 process2 打印出 3
  • 執(zhí)行 then2 打印出 5
  • 執(zhí)行第一個宏任務(wù),也就是執(zhí)行 setTimeout2 對應(yīng)代碼中的 25 - 36 行
  • 首先打印出 9
  • process 微任務(wù) 記為 process3
  • new Promise執(zhí)行resolve 打印出 11
  • then 微任務(wù) 記為 then3

當前打印順序為:1 7 6 8 2 4 3 5 9 11
當前宏任務(wù)隊列為空
當前微任務(wù)隊列:process3,then3

第四輪循環(huán):

  • 執(zhí)行所有的微任務(wù)
  • 執(zhí)行process3 打印出 10
  • 執(zhí)行then3 打印出 12

代碼執(zhí)行結(jié)束:
最終打印順序為:1 7 6 8 2 4 3 5 9 11 10 12
(請注意,node環(huán)境下的事件監(jiān)聽依賴libuv與前端環(huán)境不完全相同,輸出順序可能會有誤差)

參考文章:
JS事件循環(huán)機制(event loop)之宏任務(wù)/微任務(wù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容