Hypocrite Nush
  1. 1 Hypocrite Nush
  2. 2 One Last You Jen Bird
  3. 3 Libertus Chen-U
  4. 4 かかってこいよ NakamuraEmi
  5. 5 The Night We Stood Lyn
  6. 6 Flower Of Life 发热巫女
  7. 7 Last Surprise Lyn
  8. 8 Life Will Change Lyn
  9. 9 Warcry mpi
  10. 10 Time Bomb Veela
  11. 11 Quiet Storm Lyn
2017-12-08 12:28:41

有别于浏览器环境的Node.js Event loop

之前已经谈到过浏览器中的事件循环机制,可以简单概括为执行栈和任务队列共同实现异步队列的机制,但根据运行环境不同,这套机制也会存在差别。那么Node环境和浏览器环境的区别又是什么呢?


浏览器中的事件循环和microtask

在浏览器中的事件循环中可分为macrotask和microtask。整体代码,事件回调,XHR回调,UI渲染等都是marcotask,promise回调,MutationObserver等则是microtask。每一次marcotask执行完成都会清空式执行microtask中的任务,然后进行UI渲染。这个过程会不断重复,浏览器中的事件循环简单概括就是如此。例子如下:

setTimeout(()=>{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

setTimeout(()=>{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

这段整体代码是一个marcotask,执行完毕后没有microtask,则进入第二个marcotask——一个定时器,在其中打印timer1,然后再清理microtask——打印promise1。执行完毕后进入第三个marcotask——第二个定时器,再执行其中的microtask——打印promise2。

输出如下:

timer1
promise1
timer2
promise2

那么在Node环境执行下试试看

timer1
timer2
promise1
promise2

出现了差别,两者的事件循环机制果然是不同了。

Node中的事件循环和mircotask

在浏览器中事件循环的主任务为marcotask,但在Node中使用了自己设计的libuv作为事件驱动的跨平台抽象层,封装了一些操作系统的底层特性并对外提供统一API,其中对事件循环的主任务实现分为6个阶段,每个阶段完成时都会去清空mircotask。

timers阶段

这个阶段会去检查timer异步任务是否到达了指定下限时间,到达后会尽可能早的执行回调,但需要注意的是执行时间不是确定的。例子如下:

setTimeout(() => {
  console.log('timeout')
}, 0)

setImmediate(() => {
  console.log('immediate')
})

谁先执行是不能确定的。

I/O callbacks阶段

这个阶段执行一些系统操作错误的回调,比如TCP连接失败等。在平时开发中较少用到。

Idle,prepare阶段

仅node内部使用,开发时不用关心

poll阶段

这个阶段比较重要,在node里,任何除定时器,close外的异步方法完成时,都会将其callback加入到该阶段并立即执行。该阶段有两个功能:

  1. 处理poll队列的callback
  2. 当到达timers的下限时间时处理timers的callback

既然在这一阶段要处理两种callback,就要协调好处理时机,具体流程如下:

  • 如果poll队列中存在callback则同步执行直至队列为空。
  • 当队列为空时会去检查代码中是否设置了setImmediate。如果设置了将会结束poll阶段进入到check阶段去执行setImmediate回调。
  • 如果没有设置setImmediate,会检查代码中的timers,如果有timers达到了下限时间,事件循环会重启并进入timers阶段。
  • 如果没有设置timers,事件循环将会阻塞在该阶段等待callback入队。

check阶段

该阶段处理setImmediate的回调。

close callbacks阶段

如果一个socket阶段被突然关掉,close事件就会在这个阶段被触发,否则将通过process.nextTick触发。

process.nextTick()

最后就可以谈nextTick了,这就是node中的mircoTask,无论在以上6个阶段中的哪个阶段,只要出现了nextTick,都会在当前阶段的操作完成后处理。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「JavaScript」「Node.js」 标签。