浏览器和Node差别的事宜轮回(Event Loop)

注重

在 node 11 版本中,node 下 Event Loop 已与浏览器趋于雷同。

在 node 11 版本中,node 下 Event Loop 已与浏览器趋于雷同。

在 node 11 版本中,node 下 Event Loop 已与浏览器趋于雷同。

背景

Event Loop也是js陈词滥调的一个话题了。2月尾看了阮一峰先生的《Node定时器详解》一文后,发明没法完全对标之前看过的js事宜轮回实行机制,又查阅了一些其他材料,记为笔记,觉得不妥,总结成文。

浏览器中与node中事宜轮回与实行机制差别,不可等量齐观。
浏览器的Event loop是在HTML5中定义的范例,而node中则由libuv库完成。同时浏览《深入浅出nodeJs》一书时发明比较当时node机制已有差别,所以本文node部份针对为此文宣布时版本。强烈推荐读下参考链接中的前三篇。

浏览器环境

js实行动单线程(不斟酌web worker),一切代码皆在实行线程挪用栈完成实行。当实行线程使命清空后才会去轮询取使命行列中使命。

使命行列

异步使命分为task(宏使命,也可称为macroTask)和microtask(微使命)两类。
当满足实行前提时,task和microtask会被放入各自的行列中守候放入实行线程实行,我们把这两个行列称为Task Queue(也叫Macrotask Queue)和Microtask Queue。

  • task:script中代码、setTimeout、setInterval、I/O、UI render。
  • microtask: promise、Object.observe、MutationObserver。

详细历程

  1. 实行完主实行线程中的使命。
  2. 掏出Microtask Queue中使命实行直到清空。
  3. 掏出Macrotask Queue中一个使命实行。
  4. 掏出Microtask Queue中使命实行直到清空。
  5. 反复3和4。

即为同步完成,一个宏使命,一切微使命,一个宏使命,一切微使命……

注重

  • 在浏览器页面中能够以为初始实行线程中没有代码,每个script标签中的代码是一个自力的task,即会实行完前面的script中建立的microtask再实行背面的script中的同步代码。
  • 假如microtask一向被增加,则会继承实行microtask,“卡死”macrotask。
  • 部份版本浏览器有实行递次与上述不符的状况,多是不符合规范或js与html部份规范争执。可浏览参考文章中第一篇。
  • new Promise((resolve, reject) =>{console.log(‘同步’);resolve()}).then(() => {console.log('异步')}),即promisethencatch才是microtask,自身的内部代码不是。
  • 一般浏览器独占API未列出。

伪代码

while (true) {
  宏使命行列.shift()
  微使命行列悉数使命()
}

node环境

js实行动单线程,一切代码皆在实行线程挪用栈完成实行。当实行线程使命清空后才会去轮询取使命行列中使命。

轮回阶段

在node中事宜每一轮轮回根据递次分为6个阶段,来自libuv的完成:

  1. timers:实行满足前提的setTimeout、setInterval回调。
  2. I/O callbacks:是不是有已完成的I/O操纵的回调函数,来自上一轮的poll残留。
  3. idle,prepare:可疏忽
  4. poll:守候还没完成的I/O事宜,会因timers和超时时候等终了守候。
  5. check:实行setImmediate的回调。
  6. close callbacks:封闭一切的closing handles,一些onclose事宜。

实行机制

几个行列

除上述轮回阶段中的使命范例,我们还剩下浏览器和node共有的microtask和node独占的process.nextTick,我们称之为Microtask Queue和NextTick Queue。

我们把轮回中的几个阶段的实行行列也离别称为Timers Queue、I/O Queue、Check Queue、Close Queue。

轮回之前

在进入第一次轮回之前,会先举行以下操纵:

  • 同步使命
  • 发出异步要求
  • 计划定时器见效的时候
  • 实行process.nextTick()

最先轮回

根据我们的轮回的6个阶段顺次实行,每次拿出当前阶段中的悉数使命实行,清空NextTick Queue,清空Microtask Queue。再实行下一阶段,悉数6个阶段实行终了后,进入下轮轮回。即:

  • 清空当前轮回内的Timers Queue,清空NextTick Queue,清空Microtask Queue。
  • 清空当前轮回内的I/O Queue,清空NextTick Queue,清空Microtask Queue。
  • 清空当前轮回内的Check Queu,清空NextTick Queue,清空Microtask Queue。
  • 清空当前轮回内的Close Queu,清空NextTick Queue,清空Microtask Queue。
  • 进入下轮轮回。

能够看出,nextTick优先级比promise等microtask高。setTimeoutsetInterval优先级比setImmediate高。

注重

  • 假如在timers阶段实行时建立了setImmediate则会在此轮轮回的check阶段实行,假如在timers阶段建立了setTimeout,因为timers已掏出终了,则会进入下轮轮回,check阶段建立timers使命同理。
  • setTimeout优先级比setImmediate高,然则因为setTimeout(fn,0)的真正耽误不能够完全为0秒,能够涌现先建立的setTimeout(fn,0)而比setImmediate的回调后实行的状况。

伪代码

while (true) {
  loop.forEach((阶段) => {
    阶段悉数使命()
    nextTick悉数使命()
    microTask悉数使命()
  })
  loop = loop.next
}

测试代码

function sleep(time) {
  let startTime = new Date()
  while (new Date() - startTime < time) {}
  console.log('1s over')
}
setTimeout(() => {
  console.log('setTimeout - 1')
  setTimeout(() => {
      console.log('setTimeout - 1 - 1')
      sleep(1000)
  })
  new Promise(resolve => resolve()).then(() => {
      console.log('setTimeout - 1 - then')
      new Promise(resolve => resolve()).then(() => {
          console.log('setTimeout - 1 - then - then')
      })
  })
  sleep(1000)
})

setTimeout(() => {
  console.log('setTimeout - 2')
  setTimeout(() => {
      console.log('setTimeout - 2 - 1')
      sleep(1000)
  })
  new Promise(resolve => resolve()).then(() => {
      console.log('setTimeout - 2 - then')
      new Promise(resolve => resolve()).then(() => {
          console.log('setTimeout - 2 - then - then')
      })
  })
  sleep(1000)
})
  • 浏览器输出:

    setTimeout - 1 //1为单个task
    1s over
    setTimeout - 1 - then
    setTimeout - 1 - then - then 
    setTimeout - 2 //2为单个task
    1s over
    setTimeout - 2 - then
    setTimeout - 2 - then - then
    setTimeout - 1 - 1
    1s over
    setTimeout - 2 - 1
    1s over
  • node输出:

    setTimeout - 1 
    1s over
    setTimeout - 2 //1、2为单阶段task
    1s over
    setTimeout - 1 - then
    setTimeout - 2 - then
    setTimeout - 1 - then - then
    setTimeout - 2 - then - then
    setTimeout - 1 - 1
    1s over
    setTimeout - 2 - 1
    1s over

由此也可看出事宜轮回在浏览器和node中的差别。

参考文章

???

    原文作者:toBeTheLight
    原文地址: https://segmentfault.com/a/1190000013660033
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞