JS事件循环

JS是单线程语言,始终只有一个线程执行JS代码(Web Worker没有改变JS单线程的本质),对于异步操做,是经过事件循环(Event Loop)来实现的。浏览器

任务

每一个异步操做都是一个任务,任务分为宏任务微任务bash

  • 宏任务:script、setTimeout、setInterval、setImmediate、I/O、UI交互事件
  • 微任务:Promise(Promise的then和catch)、process.nextTick、Object.observe(已废弃)

其中, setImmediateprocess.nextTick是Node独有的,UI交互事件确定是浏览器独有的。异步

澄清两个误区

  • 事件循环和JS代码是在一个线程中运行的
  • 通过测试,无论是浏览器端仍是Node端,每一个宏任务执行完后,都会去清除微任务

特别是第二点,可能Node作了更新(当前时间是2019-01-13,Node v11.4.0),以下面代码,无论浏览器仍是Node,都会输出:1 2 3 4socket

setTimeout(function () {
  new Promise(function (resolve) {
    console.log(1);
    resolve();
  }).then(function () {
    console.log(2);
  });
});

setTimeout(function () {
  new Promise(function (resolve) {
    console.log(3);
    resolve();
  }).then(function () {
    console.log(4);
  });
});

// delay(); // 为了消除定时器最小延迟带来的影响,能够执行一下耗时操做,确保上面两个setTimeout都触发了

function delay() {
  for (let i = 0; i < 1000000000; i++) {}
}
复制代码

定时器的最小延迟问题见这里oop

差别

浏览器的事件循环是HTML5定义的规范,Node的事件循环是libuv库实现的,但截至目前(2019-01-13),二者的表现基本一致,Node的事件循环比浏览器要复杂,下面看Node的事件循环。post

Node的事件循环

Node的事件循环,每一轮有六个阶段测试

1. Timer阶段ui

执行可用的setTimeout/setInterval回调spa

2. I/O callbacks阶段线程

执行可用的I/O回调,除了setTimeoutsetIntervalsetImmediateClose callbacks都属于这个阶段。

3. idle, prepare阶段

这两个阶段主要是Node作一些内部操做,忽略。

4. Poll阶段

这是轮询阶段,若是没有可用的setTimeout/setInterval/setImmediate/Close callbacks回调,会一直停留在这个阶段,等待I/O回调。

这个阶段不会一直停留,达到必定条件后,会到下一个阶段。

5. Check阶段

专门用于执行setImmediate

6. Close callbacks阶段

关闭请求在这里执行(socket.on('close', ()=>{})),这个阶段就像一个清理阶段。

若是事件循环还活着,就继续下一轮循环。

最后

欢迎关注个人微博@狂刀二