Node.js 事件循环的完整指南

做者:Piero Borrelli

翻译:疯狂的技术宅php

原文:https://blog.logrocket.com/a-...前端

未经容许严禁转载node

每当我听到人们谈论Node.js时,就会出现不少关于到底是什么,这项技术有什么用处,以及其将来的问题。程序员

让咱们试着解决第一部分。回答这个问题最简单的方法是列出许多 Node 技术上的定义:web

  • Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎构建的 Javascript 运行时环境。
  • Node.js 使用事件驱动的非阻塞 I/O 模型,使其轻量且高效。
  • Node 包生态系统(npm)是全世界最大的开源库生态系统。

可是,这些答案并不能令我满意,由于有些东西不见了。在读了上面的要点后,你可能会认为 Node.js 只是另外一种 JavaScript 技术,可是若是你想要真正的理解它,最重要的是分析它是如何进行异步操做的和它的非阻塞 I/O 系统。面试

这是每一个 Web 开发人员应该必备的知识。npm

准确的理解 Node 在幕后的工做原理,不只会对这项技术了解的更多,还可以激发那些刚刚开始学习但还没深刻使用的人们的兴趣。segmentfault

对于已是该领域的专业人士来讲,了解它的内部和外部将使你成为一个全新、前沿的开发人员,能够根据你的需求去提升其性能。服务器

所以,为了挖掘 Node 的世界,咱们将检视其核心部分:事件循环,实际上它是负责其非阻塞 I/O 模型的部分。微信

A brief refresh on threads

简要刷新线程

在深刻了解事件循环以前,我想先在线程上花一些时间。若是你想知道这样作的必要性,我会告诉你是为了更好地理解一个概念,咱们必须先在本身的脑海中造成一个术语表,这将有助于咱们识别系统的每个部分。咱们会在稍后阅读有关事件循环如何工做,以及如何将线程的概念应用于它的内容时,这最终将具备很大的优点。

每当咱们运行一个程序时,就会为它建立一个实例,而且有一些内部调用线程与该实例相关。线程能够看做是咱们的 CPU 必须执行的操做单元。许多不一样的线程能够与程序的单个进程相关联。下面这个图能够帮你在脑海中造成这个想法:

clipboard.png

线程的简图

在讨论线程时最重要的一点是:咱们的机器如何肯定在何时处理哪一个线程?

众所周知,咱们机器的资源是有限的(CPU,RAM),所以正确的决定怎样分配它们是很是重要,换一种说法是,哪些操做优先于其余操做。这必需要作到,同时还要确操做不能消耗太多的时间 —— 没有人喜欢运行速度慢的电脑。

用于解决分配问题的机制称为 scheduling,它由操做系统中的调度程序管理。这背后的逻辑可能很是复杂,但总而言之,咱们能够将执行此操做的两种主要方式组合在一块儿:

  • 多核机器:为不一样的核心分配不一样的线程。

clipboard.png

多核机器如何处理线程

  • 使用可减小空置时间的优化逻辑: 这是最实用的方法。若是仔细研究一下线程是如何工做的,咱们将看到 OS 调度程序能够识别 CPU 什么时等待其余资源执行一个做业,由此能够分配它来同时执行其余操做。这一般发生在代价很是昂贵的 I/O 操做上,例如从硬盘读取数据。

事件循环

如今咱们已经对线程如何工做有了基本的了解,接下来解决 Node.js 事件循环逻辑。经过本文,你将了解前面那些解释背后的缘由,每一条都会对应到正确的位置上。

每当运行 Node 程序时,都会自动建立一个线程。这个线程是整个代码惟一执行的地方。在其中生成了一个被称为事件循环的东西。这个循环的做用是安排咱们惟一的线程应该在什么时间点执行哪些操做。

详细的说明

如今让咱们尝试模拟事件循环的工做原理及其工做方式。首先假设咱们正在用名为 myProgram 的文件为 Node 提供信息,而后详细了解事件循环将对其进行的操做。

clipboard.png

特别是,我将首用一个简短的图来解释,说明在事件循环 tick 过程当中发生的事情,而后再以更深刻的方式探讨这些阶段。

clipboard.png

步骤1:performChecks

不该该单纯的认为事件循环其实是一个循环。它有一个特定的条件,用来肯定循环是否须要再次迭代。事件循环的每次迭代都被称为一个 tick

事件循环执行 tick 的条件是什么?

每当执行程序时,咱们都会进行一系列须要执行的操做。这些操做主要分为三种类型:

  • 等待定时器操做(setTimeout()setInterval()setImmediate()
  • 等待处理中的操做系统任务
  • 等待须要长时间运行的操做

我稍后会详细介绍这些内容;如今让咱们记住,只要其中一个操做处于挂起状态,事件循环就会执行一个新的 tick。

步骤2:执行一个 tick

对于每一个循环迭代,能够分为如下阶段:

  • 阶段1: Node 查看其内部的挂起计时器集合,并检查传递给 setTimeout()setInterval() 的回调函数是否准备好在计时器过时的状况下被调用。
  • 阶段2: Node 查看其待处理 OS 任务的内部集合,并检查哪些回调函数已准备好被调用。一个例子是从机器的硬盘驱动器中完成了对文件的检索。
  • 阶段3: Node 暂停其执行,等待新事件发生。新事件包括:新的计时器完成,新的OS任务完成,新的待处理操做完成。
  • 阶段4: Node 检查是否已经准备好调用与 setImmediate() 函数相关函数。
  • 第5阶段: 管理关闭事件,用于清理程序状态。

关于事件循环的常见问题和错误观点

Node.js 是彻底单线程的吗?

这是对 Node.js 的一种很是广泛的误解。 Node 运行在单个线程上,可是 Node.js 标准库中包含的一些函数并非(例如 fs 模块函数),他们的逻辑运行在 Node.js 线程以外。这样作是为了保证程序的速度和性能。

这些其余线程运行在哪里?

Node.js 会使用名为 libuv 的特殊库模块来执行异步操做。此库还与 Node 的后台逻辑一块儿使用,用来管理被称为 libuv 线程池 的特殊线程池。

这个线程池由四个线程组成,用于委派对事件循环来讲过重的操做。长时间运行的任务对于事件循环而言代价过于昂贵。

那么事件循环是一种相似栈的结构?

从这个意义上说,虽然在上述过程当中涉及一些相似栈的结构,但更精确的答案是事件循环由一系列的阶段所组成,每一个阶段都有本身的特定任务,全部阶段都以循环重复的方式去处理。若是想要知道关于事件循环确切结构的更多信息,请查看此演讲

结论

了解事件循环是使用 Node.js 的重要部分,不管你是想得到有关此技术的更多看法,了解如何提升其性能,仍是找到学习新工具理由。


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章:


相关文章
相关标签/搜索