1.瀏覽器常駐線程
- GUI渲染線程:負(fù)責(zé)渲染頁面宏粤,與JavaScript引擎線程互斥,即JavaScript引擎線程執(zhí)行時間過長捕犬,可能引起頁面渲染阻塞
- JavaScript引擎線程:執(zhí)行腳本代碼舍扰,定時器及異步請求成功回調(diào),進入隊列侣诵,等待此線程進行執(zhí)行痢法。
- 定時器觸發(fā)線程:負(fù)責(zé)執(zhí)行setTimeout狱窘,setInterval異步定時的函數(shù),計時結(jié)束交給事件觸發(fā)線程财搁。
- 事件觸發(fā)線程:負(fù)責(zé)將準(zhǔn)備好的事件蘸炸,交給Js執(zhí)行,例如:定時器結(jié)束尖奔,ajax成功回調(diào)搭儒,點擊觸發(fā)的事件,添加到執(zhí)行隊列
- 異步http請求線程:執(zhí)行異步請求一類的線程提茁,例如:promise,axios,ajax淹禾。成功回調(diào)都交給事件觸發(fā)線程。
2.瀏覽器的Event Loop
執(zhí)行上下文 --> 同步壓棧執(zhí)行 -->異步掛起(宏任務(wù)/微任務(wù)) -->宏任務(wù) -->微任務(wù) -->微任務(wù) ……-->椳畋猓空 -->結(jié)束
例題分析1
setTimeout(()=>{
console.log("setTimeout1")
},0);
console.log("window");
new Promise((resolve,reject)=>{
console.log("promise");
setTimeout(()=>{
console.log('setTimeout2');
resolve();
},0);
})
.then(()=> {
console.log("promise1")
})
.then(()=>{
console.log("promise2")
});
setTimeout(()=>{
console.log('setTimeout3');
new Promise((resolve,reject)=>{
console.log("promise3");
resolve();
}).then(()=> {
console.log("promise4")
});
},0);
分析:
- window ---- script標(biāo)簽铃岔,同步
- promise ---- promise 同步
- 同步任務(wù)執(zhí)行完畢,下面執(zhí)行異步
- setTimeout1 ---- setTimeout 異步宏
- 無微任務(wù)
- setTimeout2 ---- setTimeout 異步宏
- promise1 promise2 ---- resolve鏈?zhǔn)秸{(diào)用 異步微
- setTimeout3 promise3 ---- setTimeout 異步宏
- promise4 ---promise.then() 異步微
3.Node 的 Event Loop
外部輸入數(shù)據(jù) --> 輪詢階段(poll) --> 檢查階段(check) --> 關(guān)閉事件回調(diào)階段(close callback) --> 定時器檢測階段(timer) --> I/O事件回調(diào)階段(I/O callbacks) -->閑置階段(idle, prepare) --> 輪詢階段(按照該順序反復(fù)運行)...
- timer:這個階段執(zhí)行定時器的回調(diào)峭火,包括 setTimeout和setInterval 毁习,這個階段受poll階段控制。
- I/O callbacks:這個階段執(zhí)行除了setImmediate躲胳、定時器以及close之外的所有回調(diào)蜓洪。
- idle,prepare:內(nèi)部使用
-
poll:等待新的I/O事件坯苹,一般會在這里阻塞隆檀。(最重要階段)
進入此階段,分兩種情況:- 若沒有設(shè)定timer粹湃,分兩種情況:
- (1). 當(dāng)poll隊列不為空:會執(zhí)行回調(diào)隊列恐仑,直到隊列為空,或者達到系統(tǒng)上限
- (2). 當(dāng)poll隊列為空为鳄,分兩種情況:
- (2.1) 若有setImmediate回調(diào)裳仆,則poll階段會停止,并進入check階段孤钦,執(zhí)行其回調(diào)
- (2.2)若沒有setImmediate回調(diào)歧斟,會等待回調(diào)加入隊列并立即去執(zhí)行,會有時間限定偏形,以免一直等待回調(diào)加入
- 若有設(shè)定timer静袖,且poll隊列為空的話,會去判斷timer是否超時俊扭,若超時則回到timer回調(diào)執(zhí)行階段队橙。
- check:執(zhí)行setImmediate的回調(diào)
- close callbacks:執(zhí)行像socket.on('close', ...)這種close事件的回調(diào)
異步隊列也分為宏任務(wù)與微任務(wù):
- 常見的 macro-task 比如:setTimeout、setInterval、 setImmediate捐康、script(整體代碼)该抒、 I/O 操作等盘榨。
- 常見的 micro-task 比如: process.nextTick揪荣、new Promise().then(回調(diào))等勺良。
例題分析2
const fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
setImmediate 設(shè)計在poll階段完成時執(zhí)行,即check階段倾鲫;
setTimeout 設(shè)計在poll階段為空閑時粗合,且設(shè)定時間到達后執(zhí)行,但它在timer階段執(zhí)行
先輸出 immediate乌昔,在輸出timeout隙疚。此段代碼,寫在I/O階段磕道,先check階段供屉,隊列空閑且計時到點時,在執(zhí)行timer階段溺蕉。
例題分析2
console.log('start')
setTimeout(() => {
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(() => {
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
Promise.resolve().then(function() {
console.log('promise3')
})
console.log('end')
node執(zhí)行結(jié)果:start=>end=>promise3=>timer1=>timer2=>promise1=>promise2
瀏覽器執(zhí)行結(jié)果:start=>end=>promise3=>timer1=>promise1=>timer2=>promise2
分析:
- start 伶丐,end ---- script標(biāo)簽內(nèi) 宏任務(wù)
- promise3 --- promise.then() 微任務(wù)
- timer1 --- timer階段,執(zhí)行回調(diào)疯特,將promise1放入微任務(wù)隊列
- timer2 --- timer階段哗魂,執(zhí)行回調(diào),將promise2放入微任務(wù)隊列
- promise1 --- promise.then() 微任務(wù)
- promise2 --- promise.then() 微任務(wù)
總結(jié)
瀏覽器和Node 環(huán)境下漓雅,微任務(wù)隊列的執(zhí)行時機不同
- Node端录别,microtask 在事件循環(huán)的各個階段之間執(zhí)行
- 瀏覽器端,microtask 在事件循環(huán)的 macrotask 執(zhí)行完之后執(zhí)行