1. 首先沟饥,國內(nèi)外專家理解
宏任務:script(主程序代碼) setTimeOut setInterVal setImmediate? I/O操作? UI渲染? ? ?requestAnimationFrame
微任務:promise(原生) MutationObserver process.nextTick() mutation Object.observe
script(主程序代碼)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
補充 I/O: (懂得人直接跳過) mouse clicks 蛤袒、keypresses宙搬、network events
js 內(nèi)部執(zhí)行機制:如下圖
最經(jīng)典的圖如下:
先思考一個問題?setTimeout(fn,0) 這樣的代碼舶沛,0秒后立即執(zhí)行???浸策,你太天真了娘摔!
setTimeout(fn,0)的含義是,指定某個任務在主線程最早可得的空閑時間執(zhí)行拴驮,意思就是不用再等多少秒了谍失,只要主線程執(zhí)行棧內(nèi)的同步任務全部執(zhí)行完成,棧為空就馬上執(zhí)行莹汤。
事件循環(huán)快鱼、宏任務、微任務關系如下:
2. 接著纲岭,總結(jié)抹竹。
有如下代碼:
console.log('1');// 主程序首先執(zhí)行
? ? setTimeout(function () { // 將事件插入了"任務隊列",必須等到當前代碼(執(zhí)行棧)執(zhí)行完止潮,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)
? ? ? ? console.log('2');
? ? ? ? process.nextTick(function () {
? ? ? ? ? ? console.log('3');
? ? ? ? })
? ? ? ? new Promise(function (resolve) {
? ? ? ? ? ? console.log('4');
? ? ? ? ? ? resolve();
? ? ? ? }).then(function () {
? ? ? ? ? ? console.log('5')
? ? ? ? })
? ? })
? ? new Promise(function (resolve) {
? ? ? ? console.log('6');
? ? ? ? resolve();
? ? }).then(function () {
? ? ? ? console.log('7')
? ? })
? ? process.nextTick(function () {
? ? ? ? console.log('8'); // 在當前"執(zhí)行棧"的尾部-->下一次Event Loop(主線程讀取"任務隊列")之前-->觸發(fā)process指定的回調(diào)函數(shù)
? ? })
? ? setImmediate(() => {
? ? ? ? console.info('9')? // 主線程和事件隊伍的函數(shù)執(zhí)行完成之后立即執(zhí)行? 和setTimeOut(fn,0)差不多
? ? })
? ? new Promise(function (resolve) {
? ? ? ? console.log('10');
? ? ? ? resolve();
? ? }).then(function () {
? ? ? ? console.log('11')
? ? })
? ? setTimeout(function () {
? ? ? ? console.log('12');
? ? ? ? setImmediate(() => {
? ? ? ? ? ? console.info('13')
? ? ? ? })
? ? ? ? process.nextTick(function () {
? ? ? ? ? ? console.log('14');
? ? ? ? })
? ? ? ? new Promise(function (resolve) {
? ? ? ? ? ? console.log('15');
? ? ? ? ? ? resolve();
? ? ? ? }).then(function () {
? ? ? ? ? ? console.log('16')
? ? ? ? })
? ? })
? ? process.nextTick(function () {
? ? ? ? console.log('17');
? ? })
圖片如下:
輸出的結(jié)果是:1窃判、6、10喇闸、7袄琳、11询件、9、2唆樊、4宛琅、5、8逗旁、17嘿辟、3、12片效、15红伦、16、13淀衣、14
分析:
a.?setTimeOut昙读、process.nextTick、setImmediate膨桥、promise 的區(qū)別
setTimeOut? ?將事件插入了"任務隊列"箕戳,必須等到當前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù);
process.nextTick??在當前"執(zhí)行棧"的尾部-->下一次Event Loop(主線程讀取"任務隊列")之前-->觸發(fā)process指定的回調(diào)函數(shù)
setImmediate??主線程和事件隊伍的函數(shù)執(zhí)行完成之后立即執(zhí)行 和setTimeOut(fn,0)差不多
b.?
第一步. script整體代碼被執(zhí)行国撵,執(zhí)行過程為
a. 首先陵吸,執(zhí)行script, console.log(1)
b. 創(chuàng)建setTimeout macro-task,? 由于是setTimeOut(fn,0),所有放在當前“執(zhí)行棧”的尾部介牙,默認是0.4毫秒
c. 創(chuàng)建micro-task Promise.then 的回調(diào)壮虫,并執(zhí)console.log(6),?
d. process.nextTick 創(chuàng)建micro-task,在當前"執(zhí)行棧"的尾部-->下一次Event Loop(主線程讀取"任務隊列")之前-->觸發(fā)process指定的回調(diào)函數(shù)
e. 創(chuàng)建setImmediate macro-task,主線程和事件隊伍的函數(shù)執(zhí)行完成后立即執(zhí)行
f. 創(chuàng)建micro-task Promise.then 的回調(diào),并執(zhí)console.log(10),?
g. 創(chuàng)建setTimeout macro-task,? 由于是setTimeOut(fn,0),所有放在當前“執(zhí)行椈反。”的尾部囚似,默認是0.4毫秒
h. process.nextTick 創(chuàng)建micro-task,在當前"執(zhí)行棧"的尾部-->下一次Event Loop(主線程讀取"任務隊列")之前-->觸發(fā)process指定的回調(diào)函數(shù)
第一個過程過后,已經(jīng)輸出了1 6 10 , 查看micro-task 并執(zhí)行回調(diào)线得,輸出 7饶唤,11,同步執(zhí)行?setImmediate 輸出9 ->1 6 10 7 11 9
此時第一個執(zhí)行過程執(zhí)行完贯钩,執(zhí)行setTimeOut(fn,0)募狂,輸出:2 4 5 ,里面包含了一個process.nextTick角雷,放在當前“執(zhí)行椈銮睿”的尾部,執(zhí)行 之前的process.nextTick(d勺三、h),? 輸出:8 17 雷滚,再輸出:3
再下一個事件循環(huán),輸出 12 15 16 13 14
總體輸出為:? ?1 6 10 7 11 9?2 4 5 8 17 3?12 15 16 13 14
思考:
如果把第一個setTimeout(b)改成setTimeOut(fn,1000)吗坚,如下:
輸出結(jié)果為:1 6 10 7 11 9 8 17 12 15 16 13 14 2 4 5 3?
這里可以思考祈远,為什么process.nextTick(8 17)在? g. 創(chuàng)建setTimeout macro-task,之前呆万?
再考慮如下:
輸出如下:
在process.nextTick 前面加了一個事件:setTimeout(function () {? ?console.log('18');? ? })车份,輸出是 18 在 8 之前谋减,
得出結(jié)論:
js 主程序 執(zhí)行完-> 微任務(全部執(zhí)行)->setImmediate->(setTimeOut(fu,0)與process.nextTick,那個在前面就先執(zhí)行誰躬充,完成一個事件循環(huán))->再到下一個事件循環(huán)
3. 推薦的官方文檔
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly
http://lynnelv.github.io/js-event-loop-nodejs
https://zhuanlan.zhihu.com/p/34182184
https://zhuanlan.zhihu.com/p/33058983
https://link.juejin.im/?target=https%3A%2F%2Fjakearchibald.com%2F2015%2Ftasks-microtasks-queues-and-schedules
https://link.juejin.im/?target=https%3A%2F%2Fhackernoon.com%2Funderstanding-js-the-event-loop-959beae3ac40
https://link.juejin.im/?target=https%3A%2F%2Fwww.oschina.net%2Ftranslate%2Funderstanding-process-next-tick
https://link.juejin.im/?target=https%3A%2F%2Fhtml.spec.whatwg.org%2Fmultipage%2Fwebappapis.html
https://link.juejin.im/?target=https%3A%2F%2Fnodejs.org%2Fen%2Fdocs%2Fguides%2Fevent-loop-timers-and-nexttick
https://link.juejin.im/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FWindow%2FrequestAnimationFrame
https://link.juejin.im/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FMutationObserver