乐趣区

关于node.js:深入理解事件循环机制

微信公众号:[前端一锅煮]
一点技术、一点思考。
问题或倡议,请公众号留言。

抛在后面的问题:

js 是单线程的如何做到异步?

事件循环的过程是怎么的?

macrotask 和 microtask 是什么,它们有何区别?

node.js 的事件循环是怎么的,和浏览器的事件循环有何区别?

过程和线程

浏览器是多过程的,具体蕴含的过程有:

  • Browser 过程:浏览器的主过程(负责协调、主控),只有一个;
  • 第三方插件过程:每种类型的插件对应一个过程,仅当应用该插件时才创立;
  • GPU 过程:最多一个,用于 3D 绘制;
  • 浏览器渲染过程(内核):默认每个 tab 页面一个过程,互不影响,管制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白 tab 会合并成一个过程)。

其中浏览器渲染过程就是前端页面次要用到的过程,蕴含的线程有:

  • GUI 渲染线程(负责渲染页面,解析 HTML,CSS 形成 DOM 树,和 JS 引擎互斥)
  • JS 引擎线程
  • 事件触发线程
  • 定时器触发线程
  • Http 申请线程等次要线程

对于执行中的线程:

主线程:也就是 js 引擎执行的线程,此线程只有一个,页面渲染、函数解决都在这个主线程上执行。

工作线程:也称幕后线程,这个线程可能存在于浏览器或 js 引擎内,与主线程是离开的,解决文件读取、网络申请等异步事件。

事件循环

所有的工作能够分为同步工作和异步工作,同步工作,顾名思义,就是立刻执行的工作,同步工作个别会间接进入到主线程中执行。而异步工作,就是异步执行的工作,比方 ajax 网络申请,setTimeout 定时函数等都属于异步工作,异步工作会通过工作队列的机制 (先进先出的机制) 来进行协调。

同步和异步工作别离进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入工作队列。主线程内的工作执行结束为空,会去工作队列读取对应的工作,推入主线程执行。上述过程的一直反复就是咱们说的 Event Loop (事件循环)。

在事件循环中,每进行一次循环操作称为 tick,其要害的步骤能够总结如下:

  1. 一开始整个脚本作为一个宏工作执行
  2. 执行过程中同步代码间接执行,宏工作进入宏工作队列,微工作进入微工作队列
  3. 以后宏工作执行完出队,读取微工作列表,有则顺次执行,直到全副执行完
  4. 读取宏工作列表,有则顺次执行,直到全副执行完
  5. 执行浏览器 UI 线程的渲染工作
  6. 查看是否有 Web Worker 工作,有则执行
  7. 执行完本轮的宏工作,回到第 2 步,持续依此循环,直到宏工作和微工作队列都为空

宏工作与微工作

JS 引擎把所有工作分成两类,一类叫宏工作(macrotask),一类叫微工作(microtask)。

宏工作次要蕴含:

  • js(整体代码)
  • I/O、UI 渲染
  • MessageChannel、postMessage
  • setImmediate(Node.js 环境)
  • setTimeout、setInterval
  • requestAnimationFrame 属于 GUI 引擎,产生在渲染过程的重绘重排局部,在 UI 渲染之前执行

微工作次要蕴含:

  • process.nextTick(Node.js 环境)
  • MutaionObserver(浏览器环境)
  • Promise

先执行完微工作再执行宏工作。

Node.js 的事件循环

事件循环是 Node.js 解决非阻塞 I/O 操作的机制。目前大多数内核都是多线程的,它们可在后盾解决多种操作。当其中的一个操作实现的时候,内核告诉 Node.js 将适宜的回调函数增加到轮询队列中期待机会执行。

当 Node.js 启动后,它会初始化事件循环,解决已提供的输出脚本,它可能会调用一些异步的 API、调度定时器,或者调用 process.nextTick(),而后开始处理事件循环。

以下是 Node.js 事件循环程序:

  1. timers:执行到期的 setTimeout、setInterval 回调。
  2. pending callbacks:挂起的回调函数,执行提早到下一个循环迭代的 I/O 回调。对某些零碎操作(如 TCP 谬误类型)执行回调。
  3. idle, prepare:闲暇 筹备,node 零碎外部应用。
  4. poll:检索新的 I/O 事件。执行 I/O(例如文件、网络)的回调,除了 close,定时器和 setImmediate 以外的所有回调。其余状况 node 将在适当的时候在此阻塞。
  5. check:执行 setImmediate 回调。
  6. close callbacks:执行 close 事件回调,如 socket.on(‘close’, …)、http close 等。

下面是宏工作的执行程序,Node.js 也是先执行微工作再执行宏工作。微工作次要有 process.nextTick 和 promise,其中 process.nextTick 先执行。

最初,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列外面,期待主执行栈来执行的,并没有专门的异步执行线程。

退出移动版