熟悉宏任務和微任務以及js(nodejs)事件循環機制,在寫業務代碼還是自己寫庫,或者看源碼都是那么重要
看了部分文檔,自己總結和實踐了一下
js中同步任務、宏任務和微任務介紹
同步任務: 普通任務
宏任務(異步任務,包括各種DOM事件) 來自於h5規范
分類: I/O(網絡,文件,數據庫I/O) setTimeout setInterval requestAnimationFrame(下次頁面重繪前所執行的操作, 瀏覽器環境才有) setImmediate(nodejs才有)
- 宏任務所處的隊列:宏任務隊列
- 第一個宏任務隊列只執行一個任務,執行主線程js代碼,
- 宏任務隊列可以有多個任務
微任務(異步任務) 來自於h5規范
分類: new Promise().then(回調)、 process.nextTick()(nodejs才有)
- 微任務所處的隊列:微任務隊列
- 只有一個微任務隊列
- 在上一個宏任務隊列執行完畢后,如果有微任務隊列就會執行微任務隊列中的所有任務;
執行順序
當主線程js代碼(屬於宏任務第一隊列)執行完畢后,如果有微任務,則優先執行微任務(process.nextTick優先執行),然后才執行宏任務
nodejs事件輪詢(循環)機制(宏任務)介紹(借助libuv(c / c++)庫實現)
執行順序:
- timer: 定時器階段 計時和執行到點的回調函數 settimeout
- pending callbacks 處理某些系統操作(TCP連接錯誤等)
- idle prepare 准備工作(nodejs才有)
- poll 輪詢階段(輪詢隊列,可以理解為普通異步任務,如網絡請求) 先進先出、依次同步取出輪詢隊列中第一個回調函數執行/知道隊列為空 或者 達到系統最大限制
如果隊列為空,並且設置過setImmediate,直接進入下一個check階段(未設置:停留在當前poll階段等待,直到隊列添加了回調函數) - check階段,查階段,執行setImmediate(nodejs才有)
- close callbacks 關閉階段,執行close事件
舉個栗子
console.log('start') // 順序1 主線程同步任務
setTimeout(() => { // 順序6 宏任務,按照事件輪詢機制執行
console.log('setTimeout')
}, 0);
new Promise((resolve, reject) => { // new promise 屬於同步主線程任務,優先執行 順序2
for (let i = 0; i < 5; i++){ // 同步執行主線程任務
console.log(i)
}
setTimeout(() => {
console.log('promise settimeout') // 順序7 次於上一個異步任務
}, 0);
resolve()
}).then(() => {
console.log('Promise回調執行完畢') // 順序5 new Promise().then(回調)屬於微任務 優於宏任務執行
})
setImmediate(function (params) { // 順序8 宏任務執行,按照事件輪詢機制執行
console.log('setImmediate')
})
process.nextTick(function (params) { // 特例:在同步任務結束后,微任務如果有process.nextTick,優先執行 順序4
console.log('nextTick')
})
console.log('main process end') // 順序3 主線程同步任務
// start
// 0
// 1
// 2
// 3
// 4
// main process end
// nextTick
// Promise回調執行完畢
// setTimeout
// setImmediate
/**
setImmediate 和 setTimeout 的優先級
一般根據定時器setTimeout waittime決定
*/
var t1 = +new Date();
setImmediate(function () {
console.log('1');
});
setTimeout(function () {
console.log('2');
},20);
console.log('3');
var t2 = +new Date();
console.log('time: ' + (t2 - t1));
//輸出
// 3
// time: 23
// 2
// 1
var t1 = +new Date();
setImmediate(function () {
console.log('1');
});
setTimeout(function () {
console.log('2');
},30);
console.log('3');
var t2 = +new Date();
console.log('time: ' + (t2 - t1));
//輸出
// 3
// time: 23
// 1
// 2