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  

相关文章
相关标签/搜索