天天看點

javascript --- > event loop栗子1JS的執行順序常見的宏任務和微任務栗子1說明:栗子2浏覽器中的事件循環Node.js的Event Loop過程:setTimeout 和 setImmediate栗子3

栗子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');
           
javascript --- > event loop栗子1JS的執行順序常見的宏任務和微任務栗子1說明:栗子2浏覽器中的事件循環Node.js的Event Loop過程:setTimeout 和 setImmediate栗子3
  • 說明: 在"promise2"和"setTimeoout"之間有"<· undefined"
  • “<· undefined”: 其實是進入了下一輪事件循環
  • 可視化展示: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

JS的執行順序

  • 原則:
  1. 事件循環過程中,每次隻執行1個宏任務
  2. 在執行宏任務之前,首先檢查微任務隊列是否為空.若存在微任務,則先執行微任務

常見的宏任務和微任務

  • 常見的宏任務: setTimeout、setInterval、setImmediate(Node)、requestAnimationFrame(浏覽器)、I/O、UI rendering(浏覽器)
  • 常見的微任務: process.nextTick(Node)、promise.then()、Obeject.observe、MutationObeserve

栗子1說明:

  • 在了解了執行順序以及宏/微任務之後,再看上面的栗子1:
  • console.log('script start')

    : 同步任務, 輸出 ‘script start’
  • setTimeout(function(){ console.log('setTimeout') },0)

    : 這是一個宏任務,會将函數

    function(){ console.log('setTimeout') }

    推到宏任務隊列中
  • Promise.resovle().then(function(){console.log('promise1')}).then(function(){console.log('promise2')})

    : 2個微任務,一次推入微任務隊列
  • console.log('script end')

    : 同步任務,輸出 ‘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")
})
           
javascript --- &gt; event loop栗子1JS的執行順序常見的宏任務和微任務栗子1說明:栗子2浏覽器中的事件循環Node.js的Event Loop過程:setTimeout 和 setImmediate栗子3
  • 說明:
  1. Promise是宏任務,其裡面的函數是同步的.即會執行

    console.log('resolve')

  2. nextTick可以了解為在其他類型微任務的前面入隊.

浏覽器中的事件循環

  • 執行全局Script的同步代碼
  • 執行microtask任務
  • 從宏任務隊列中取出隊首一個任務
  • 執行該任務
  • 任務執行完畢,檢查是否有微任務(有則執行,否則執行第一步)

Node.js的Event Loop過程:

  1. 執行全局Script的同步代碼
  2. 執行microtask微任務,先執行所有 Next Tick Queue中的所有任務,再執行Other Microtask Queue中的所有任務
  3. 開始執行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');
})
           
javascript --- &gt; event loop栗子1JS的執行順序常見的宏任務和微任務栗子1說明:栗子2浏覽器中的事件循環Node.js的Event Loop過程:setTimeout 和 setImmediate栗子3

同樣的代碼執行的結果不确定:

  • setTimeout/setInterval的第二個參數取值範圍是: [1, 2^31 -1],如果超過這個範圍就會初始化為1,即 setTimeout(fn, 0) === setTimeout(fn, 1);
  • setTimeout的回調函數再timer階段執行,setImmediate的回調函數再check階段執行,event loop的開始會檢查timer階段,但是再開始之前到timer階段會消耗一定時間,就會出現以下情況:
  1. timer前的準備時間超過1ms, 滿足loop -> time >=1, 則執行timer階段(setTimeout)的回調函數
  2. 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);
           
javascript --- &gt; event loop栗子1JS的執行順序常見的宏任務和微任務栗子1說明:栗子2浏覽器中的事件循環Node.js的Event Loop過程:setTimeout 和 setImmediate栗子3
  • 說明:
  1. 首先執行script代碼,輸出3468
  2. 執行nextTick任務,輸出7
  3. 執行microtask, 輸出5, start: 15.232ms
  4. 如果事件大于10ms,則執行宏任務 “輸出2”, 否則輸出"1"

繼續閱讀