JavaScript event loop事件循环 macrotask与microtask

macrotask  姑且称为宏任务,在很多上下文也被简称为task。例如:

 

 setTimeout,

setInterval,

setImmediate,

I/O,

UI rendering.

 

microtask 微任务,也称job。例如:


process.nextTick,

Promise(原生),

Object.observe,

MutationObserver

备注:同时需要注意的是,在 ES 当中称 microtask 为 “jobs”。比如 ES6标准 8.4节当中的 “EnqueueJob” 意思指添加一个 microtask。

 

 

 

看看下面的实例:

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');

  

1、首先 setTimeout A 被加入到事件队列中  ==>  此时macrotasks中有[‘A’];

2、obj.func()执行时,setTimeout B 被加入到事件队列中  ==> 此时macrotasks中有[‘A’,‘B’];

3、接着return一个Promise对象,Promise 新建后立即执行 执行console.log('C'); 控制台首次打印‘C’;

4、然后,then方法指定的回调函数,被加入到microtasks队列,将在当前脚本所有同步任务执行完才会执行。 ==> 此时microtasks中有[‘D’];

5、然后继续执行当前脚本的同步任务,故控制台第二次输出‘E’;

6、此时所有同步任务执行完毕,如上所述先检查microtasks队列完成其中所有任务,故控制台第三次输出‘D’;

7、最后再执行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  

实现 参考popper   @version 1.12.6

var isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
var timeoutDuration = 0;
for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
  if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
    timeoutDuration = 1;
    break;
  }
}

function microtaskDebounce(fn) {
  var called = false;
  return function () {
    if (called) {
      return;
    }
    called = true;
    Promise.resolve().then(function () {
      called = false;
      fn();
    });
  };
}

function taskDebounce(fn) {
  var scheduled = false;
  return function () {
    if (!scheduled) {
      scheduled = true;
      setTimeout(function () {
        scheduled = false;
        fn();
      }, timeoutDuration);
    }
  };
}

var supportsMicroTasks = isBrowser && window.Promise;

/**
* Create a debounced version of a method, that's asynchronously deferred
* but called in the minimum time possible.
*
* @method
* @memberof Popper.Utils
* @argument {Function} fn
* @returns {Function}
*/
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;

  调用

 this.update = debounce(this.update.bind(this));

  

 

 

 

 

 
posted @ 2019-04-19 15:20  surfaces  阅读(302)  评论(0编辑  收藏  举报