This website requires JavaScript.

浏览器Event loop 与 node Event loop 区别 笔记

2019.03.16 10:03字数 2411阅读 38喜欢 0评论 0

其实从设计上来说,这两个其实是两个东西,都叫Event loop 而已

一、浏览器的Event loop 是 html5的规范,各个浏览器自由发挥去实现

执行栈(单线程)同步执行同步代码 ==> 遇到异步交给其他线程(web api, 定时器,异步http请求等),执行完将回调放入队列中(宏任务队列,微任务队列) ==> 执行栈为空时,执行一个宏任务,清空微任务 ==> 渲染UI ==> 执行一个宏任务 , 清空微任务 ==> 渲染UI ==> …….一直到全部执行所有的任务

二、node 的Event loop是通过 libuv 线程池来实现的,主要分为6个阶段

Libuv 是基于事件驱动的库,为node提供跨平台的异步I/O能力

event loop的每一次循环都需要依次经过上述的阶段。 每个阶段都有自己的callback队列,每当进入某个阶段,都会从所属的队列中取出callback来执行,当队列为空或者被执行callback的数量达到系统的最大数量时,进入下一阶段。这六个阶段都执行完毕称为一轮循环

注意点
(1) setTimeout 和 setImmediate*

二者非常相似,区别主要在于调用时机不同。

  • setImmediate 设计在poll阶段完成时执行,即check阶段;
  • setTimeout 设计在poll阶段为空闲时,且设定时间到达后执行,但它在timer阶段执行

其二者的调用顺序取决于当前event loop的上下文

一、如果他们在异步i/o callback之外调用,其执行先后顺序是不确定的

setTimeout(function timeout () {
  console.log('timeout');
},0);
setImmediate(function immediate () {
  console.log('immediate');
});
  • 对于以上代码来说,setTimeout 可能执行在前,也可能执行在后。
  • 首先 setTimeout(fn, 0) === setTimeout(fn, 1),这是由源码决定的 进入事件循环也是需要成本的,如果在准备时候花费了大于 1ms 的时间,那么在 timer 阶段就会直接执行 setTimeout 回调
  • 如果准备时间花费小于 1ms,那么就是 setImmediate 回调先执行了

二、但当二者在异步i/o callback内部调用时,总是先执行setImmediate,再执行setTimeout

const fs = require('fs')
fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0)
    setImmediate(() => {
        console.log('immediate')
    })
})
// immediate
// timeout

在上述代码中,setImmediate 永远先执行。因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行完毕后队列为空,发现存在 setImmediate 回调,所以就直接跳转到 check 阶段去执行回调了。

(2) process.nextTick*

这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。

setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(function () {
    console.log('promise1')
  })
}, 0)
process.nextTick(() => {
  console.log('nextTick')
  process.nextTick(() => {
    console.log('nextTick')
    process.nextTick(() => {
      console.log('nextTick')
      process.nextTick(() => {
        console.log('nextTick')
      })
    })
  })
})
// nextTick=>nextTick=>nextTick=>nextTick=>timer1=>promise1