栗子1
- 求下面函數的輸出
console.log('script start');
setTimeout(() => {
console.log('setTimeoout');
}, 0);
Promise.resolve().then(function(){
console.log('promise1');
}).then(function(){
console.log('promise2');
})
console.log('script end');

- 說明: 在"promise2"和"setTimeoout"之間有"<· undefined"
- “<· undefined”: 其實是進入了下一輪事件循環
- 可視化展示: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
JS的執行順序
- 原則:
- 事件循環過程中,每次隻執行1個宏任務
- 在執行宏任務之前,首先檢查微任務隊列是否為空.若存在微任務,則先執行微任務
常見的宏任務和微任務
- 常見的宏任務: setTimeout、setInterval、setImmediate(Node)、requestAnimationFrame(浏覽器)、I/O、UI rendering(浏覽器)
- 常見的微任務: process.nextTick(Node)、promise.then()、Obeject.observe、MutationObeserve
栗子1說明:
- 在了解了執行順序以及宏/微任務之後,再看上面的栗子1:
-
: 同步任務, 輸出 ‘script start’console.log('script start')
-
: 這是一個宏任務,會将函數setTimeout(function(){ console.log('setTimeout') },0)
推到宏任務隊列中function(){ console.log('setTimeout') }
-
: 2個微任務,一次推入微任務隊列Promise.resovle().then(function(){console.log('promise1')}).then(function(){console.log('promise2')})
-
: 同步任務,輸出 ‘script end’console.log('script end')
- 到了這裡,開始執行事件循環的下一輪,根據原則2.先檢擦微任務隊列是否為空,此時不為空,于是執行隊列的第一個(即輸出 ‘promise1’),然後出隊,在檢查微任務隊列是否為空(此處不為空,故輸出’promise2’,出隊),在檢查…
- 當微任務隊列為空,代表目前事件循環結束,是以會輸出一個傳回值,此處未設定,故輸出"<· undefined"
- 從宏任務隊列中讀取,輸出(“setTimeout”)
栗子2
- 求以下函數的輸出結果
new Promise(resolve => {
console.log("resolve")
resolve()
})
.then(() => console.log("promise then..."))
setImmediate(() => {
console.log("set immediate...")
})
setTimeout(() => {
console.log("set Timeout ...");
}, 0);
process.nextTick(() => {
console.log("nextTick")
})
- 說明:
- Promise是宏任務,其裡面的函數是同步的.即會執行
console.log('resolve')
- nextTick可以了解為在其他類型微任務的前面入隊.
浏覽器中的事件循環
- 執行全局Script的同步代碼
- 執行microtask任務
- 從宏任務隊列中取出隊首一個任務
- 執行該任務
- 任務執行完畢,檢查是否有微任務(有則執行,否則執行第一步)
Node.js的Event Loop過程:
- 執行全局Script的同步代碼
- 執行microtask微任務,先執行所有 Next Tick Queue中的所有任務,再執行Other Microtask Queue中的所有任務
- 開始執行macrotask宏任務,共6個階段,從第1個階段開始執行相應每一個階段macrotask中的所有任務,六個階段: Timers Queue -> 步驟2 -> I/O Queue -> 步驟2 -> Check Queue -> 步驟2 -> Close Callback Queue -> 步驟2 -> Timers Queue…
- MacroTask包括: setTimeout、setInterval、setImmediate(Node)、requestAnimation(浏覽器)、IO、UI rendering(浏覽器)
- MicroTask包括:s process.nextTick(Node)、Promise.then、Object.observe、MutationObserver
setTimeout 和 setImmediate
- setImmediate():方法用于中斷長時間運作的操作,并在完成其他操作後立即運作回調函數
- 栗子:
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
})
同樣的代碼執行的結果不确定:
- setTimeout/setInterval的第二個參數取值範圍是: [1, 2^31 -1],如果超過這個範圍就會初始化為1,即 setTimeout(fn, 0) === setTimeout(fn, 1);
- setTimeout的回調函數再timer階段執行,setImmediate的回調函數再check階段執行,event loop的開始會檢查timer階段,但是再開始之前到timer階段會消耗一定時間,就會出現以下情況:
- timer前的準備時間超過1ms, 滿足loop -> time >=1, 則執行timer階段(setTimeout)的回調函數
- timer前的準備時間小于1ms,則先執行check階段(setImmediate)的回調函數,下一次event loop執行timer階段(setTimeout)的回調函數
栗子3
console.time("start");
setImmediate(function() {
console.log(1);
});
setTimeout(function() {
console.log(2);
}, 10);
new Promise(function(resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function() {
console.log(5);
console.timeEnd("start")
});
console.log(6);
process.nextTick(function() {
console.log(7);
});
console.log(8);
- 說明:
- 首先執行script代碼,輸出3468
- 執行nextTick任務,輸出7
- 執行microtask, 輸出5, start: 15.232ms
- 如果事件大于10ms,則執行宏任務 “輸出2”, 否則輸出"1"