node.js与浏览器中的EventLoop事件循环是一样的吗?

什么是EventLoop(事件循环)?

event loop 是一种计算机系统运行的机制,由于JavaScript是一门自诞生起就是单线程运行的语言,当按顺序执行代码在代码当中出现异步时,内核会将异步任务加入到轮询队列中,等当前任务执行完成后,然后再开始执行轮询队列中的任务,这个过程就是event loop。

这里从2个方向来讨论:

  1. node.js
  2. 浏览器

 

node.js

node.js中event loop 分为六个阶段:

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

我们可以简化理解为三个主要阶段:

  1. timers
  2. poll
  3. check

timers

计时器实际上是在指定多久以后可以执行某个回调函数,而不是指定某个函数的确切执行时间。当指定的时间达到后,计时器的回调函数会尽早被执行。如果操作系统很忙,或者 Node.js 正在执行一个耗时的函数,那么计时器的回调函数就会被推迟执行。

poll

poll 阶段有两个功能:

  1. 如果发现计时器的时间到了,就绕回到 timers 阶段执行计时器的回调。
  2. 然后再,执行 poll 队列里的回调。

check

这个阶段允许开发者在 poll 阶段结束后立即执行一些函数。如果 poll 阶段空闲了,同时存在 setImmediate() 任务,event loop 就会进入 check 阶段。

总结:

  • node.js中的事件循环顺序:timers => poll => check => timers...
  • setTimeout/setInterval 任务在timers阶段。
  • setImmediate 任务在check阶段。
  • process.nextTick 任务在当前任务后执行。例如当前任务为timers,nextTick则在timers后poll前执行。
  • Promise.then() then后面的函数执行顺序为当前后面,与nextTick同理。
  • async await 即Promise的语法糖,改写为Promise即可,顺序为当前后面。
  • process.nextTick 可理解为微任务,timers\poll\check可理解为宏任务。

 

浏览器

浏览器的event loop理解起来比较容易,两个:

  1. 宏任务
  2. 微任务

宏任务

setTimeout/setInterval 为宏任务(等一会儿执行)。

微任务

promise.then() then后面接的函数为微任务(马上执行)。

总结:

  1. 代码顺序执行时,微任务比宏任务快。
  2. new Promise(resolve, reject) 中的resolve与reject 函数是立即执行的,不在event loop队列中。
  3. 做event loop 相关的题目最好用笔以图表的形式写出来,纯靠推理很容易错的。

 

EventLoop面试题

请问在浏览器中,log的执行顺序为?

async function async1() {
    console.log(1)
    await async2()
    console.log(2)
}

async function async2() {
    console.log(3)
}

async1()

new Promise(function(resolve){
    console.log(4)
    resolve()
}).then(function() {
    console.log(5)
})

我们逐行分析代码:

  1. 当浏览器开始从上往下逐行解析js代码时,会先打印1,因为这是同步代码。在async声明的函数中,await函数前的代码都是同步代码,只有当await async2() 执行后,下面的代码才是异步代码。
  2. 第二个log参数到底是2还是3呢?结果为3,之前我们说了await async2() 函数执行后才会是异步代码,因此async2() 就是同步执行的,因此log为2。
  3. 第三个参数log值会是2吗?不是的,因为await async2()以下的代码都是异步执行的,为微任务。async语法糖可改写为 async2().then(fn1),fn1函数即为async2() 后面所有的代码。因此2将暂时放到微任务队列中。
  4. new Promise() 括号中的函数是同步执行的,因此log为4。
  5. 第4点已经说了,then出来的函数为微任务,因此将5添入到微任务队列中。

这时候,log的执行顺序就出来了,先是同步打印出1、3、4,再微任务队列中打印出2、5。这里没有宏任务,如有宏任务参与可增加一个宏任务队列,总的顺序遵循:同步>微任务>宏任务。

最后答案为:1、3、4、2、5

 

 

你可能感兴趣的:(JS,Node.js,event,loop,EventLoop,事件循环,js中的事件循环,js中的EventLoop)