JavaScript event loop事件循環 macrotask與microtask

macrotask  姑且稱爲宏任務,在不少上下文也被簡稱爲task。例如:

 

 setTimeout,javascript

setInterval,java

setImmediate,node

I/O,typescript

UI rendering.promise

 

microtask 微任務,也稱job。例如:


process.nextTick, 函數

Promise(原生),spa

Object.observe,code

MutationObserverorm

備註:同時須要注意的是,在 ES 當中稱 microtask 爲 「jobs」。好比 ES6標準 8.4節當中的 「EnqueueJob」 意思指添加一個 microtask。server

 

 

 

看看下面的實例:

setImmediate(function(){
    console.log(1);
},0);
setTimeout(function(){
    console.log(2);
},0);
new Promise(function(resolve){
    console.log(3);
    resolve();
    console.log(4);
}).then(function(){
    console.log(5);
});
console.log(6);
process.nextTick(function(){
    console.log(7);
});
console.log(8);
 
//輸出結果是3 4 6 8 7 5 2 1

 

看看另外一題目

setTimeout(()=>{
    console.log('A');
},0);
var obj={
    func:function () {
        setTimeout(function () {
            console.log('B')
        },0);
        return new Promise(function (resolve) {
            console.log('C');
            resolve();
        })
    }
};
obj.func().then(function () {
    console.log('D')
});
console.log('E');

  

一、首先 setTimeout A 被加入到事件隊列中  ==>  此時macrotasks中有[‘A’];

二、obj.func()執行時,setTimeout B 被加入到事件隊列中  ==> 此時macrotasks中有[‘A’,‘B’];

三、接着return一個Promise對象,Promise 新建後當即執行 執行console.log('C'); 控制檯首次打印‘C’;

四、而後,then方法指定的回調函數,被加入到microtasks隊列,將在當前腳本全部同步任務執行完纔會執行。 ==> 此時microtasks中有[‘D’];

五、而後繼續執行當前腳本的同步任務,故控制檯第二次輸出‘E’;

六、此時全部同步任務執行完畢,如上所述先檢查microtasks隊列完成其中全部任務,故控制檯第三次輸出‘D’;

七、最後再執行macrotask的任務,而且按照入隊列的時間順序,控制檯第四次輸出‘A’,控制檯第五次輸出‘B’。


結果 C  E  D A B

 

 

 

setTimeout(function timeout () {
      console.log('timeout');
    },0);

    setImmediate(function immediate () {
      console.log('immediate');
    });

結果 

immediate
 timeout

 

 

setInterval(function timeout () {
      console.log('setInterval');
    },0);
            
            setTimeout(function timeout () {
      console.log('timeout');
    },0);

    setImmediate(function immediate () {
      console.log('immediate');
    });

結果:

immediate
 setInterval
 timeout
 setInterval  

 

 

setTimeout(function timeout () {
      console.log('timeout');
    },0);setInterval(function timeout () {
      console.log('setInterval');
    },0);

  

timeout
setInterval

 

另外一個

setTimeout(function timeout () {
  console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});

process.nextTick(function immediate () {
  console.log('nickTick');
});

結果

nextTick
timeout
immediate

process.nextTick像是一個插入的tick. 生成了一個新的週期. 說白了, 是一個插隊行爲.

關於micro-task和macro-task的執行順序,可看下面這個例子(來自《深刻淺出Node.js》):
//加入兩個nextTick的回調函數
process.nextTick(function () {
    console.log('nextTick延遲執行1');
});
process.nextTick(function () { 
    console.log('nextTick延遲執行2');
});
// 加入兩個setImmediate()的回調函數
setImmediate(function () {
    console.log('setImmediate延遲執行1'); 
    // 進入下次循環 
    process.nextTick(function () {
        console.log('強勢插入');
    });
});
setImmediate(function () {
    console.log('setImmediate延遲執行2'); 
});
 
console.log('正常執行')

書中給出的執行結果是:

正常執行
nextTick延遲執行1
nextTick延遲執行2
setImmediate延遲執行1
強勢插入
setImmediate延遲執行2

 

process.nextTick在兩個setImmediate之間強行插入了。
但運行這段代碼發現結果倒是這樣:

正常執行
nextTick延遲執行1
nextTick延遲執行2
setImmediate延遲執行1
setImmediate延遲執行2
強勢插入

樸老師寫那本書的時候,node最新版本爲0.10.13,而個人版本是6.x  

相關文章
相關標籤/搜索