Node.js 与JavaScript 的关系是什么?
随着Node.js 的问世,前端开发者的触角也逐渐蔓延到后端,甚至透过Electron.js这类强大的套件,也可以制作出完整的桌面GUI 应用程式;藉由Node.js,前端开发者得以使用较为熟悉的JavaScript 为敲门砖,逐步的拓展自己的技术守备范围。但为什么为了网页而生的语言可以透过Node.js 跑在伺服器端呢?要解开这个问题,就得从认识Node.js 出发。
Node.js 是什么呢?
根据官网的说法:Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine .
「runtime」 指的是执行环境,就如同网页上的JavaScript 是在浏览器的JavaScript 引擎上执行,Node.js 就是一个能执行JavaScript 的环境,而V8 则是主流浏览器— Google Chrome 的JavaScript 引擎,负责解析、执行JavaScript,也就是负责实践ECMAScript 规范中定义的部份;另外,V8 是开源的专案,有兴趣的读者可以参照Google Git — V8。
Node.js 以V8 为核心,加上一系列C/C++ 的套件,成功的让Server 端也可以执行JavaScript。

优点
但是,后端语言已经这么多了,为什么还要大费周章的将JavaScript 移植到Server 端呢?
这是因为JavaScript 是一个事件驱动的语言,透过事件回圈,能让执行绪几乎不会被卡住;而这样的特性,非常适合用来接收高并发(High Concurrency)的请求。
例如在传统的伺服器中,每个使用者的连接都会产生一个新的执行绪(看实作,不一定),并占据一定的效能,伺服器在高并发的情况下,很容易就会由于应接不暇而无法服务新的流量;但Node.js 会将每个request 变成事件回圈中待处理的事件,主执行绪只负责承接、转抛、回应,并持续的在事件回圈中循环,一切都以事件为核心在驱动程式运行,自然也就不会出现执行绪卡死的现象。
当然,如果是商业逻辑复杂的后端程式,效能瓶颈不在流量的服务,Node.js 就无用武之地;但在设计需要承接高流量,且处理逻辑不太复杂时,Node.js 可能就会是个可以考虑的选项。
功能
前面提到,Node.js 就是一个可以执行JavaScript 的环境,而这个环境除了提供浏览器Web api 实作的setTimeout、setInterval、console之外,也因为执行环境不同,有另外一系列的API 供开发者使用,例如可以读写档案的fs、处理网路请求的http、做加解密杂凑处理的crypto、设定丛集的 cluster 等等。
详细的使用说明,可以参考Node.js 的官网文件
事件
由于JavaScript 拥有单执行绪的特性,且为了让执行绪不会被需要等待的同步程式卡住,必须透过事件回圈的机制来实现这个目标。如果各位读者还有印象,我们在 这篇 有聊过浏览器中JavaScript 的事件回圈,其中有很大一部分是由浏览器完成的;在Node.js 中则透过libuv来实现这部分的机制。不同于浏览器的事件回圈,Node.js 中的事件回圈大致会有以下几个阶段:
- timers:执行 setTimeout setInterval 给的callback
- pending callbacks:执行被延迟到下一个事件回圈的I/O Callback
- idle, prepare:Node.js 内部专用的阶段
- poll:检索新的I/O events,执行I/O callbacks
- check:执行 setImmediate 给的callback。
- close callbacks:执行关闭资源的callback,例如socket.on('close', ...)
相对于浏览器的事件回圈多了好多个阶段,但其实只是把所有的callback 分成了四种:timers、I/O events、immediates、close handlers,并依照顺序轮流执行,其他在概念上还是一样的:每个阶段有自己的Queue,轮到它时清空Queue,到下个阶段,周而复始。
为避免主执行绪阻塞,poll 阶段可以设定执行上限,到达上限时就会将Queue 内的东西移交到pending callbacks 阶段的Queue 中,下一个事件回圈时再接续执行。
比较需要注意的地方是,微任务伫列(microtask queue)在每个阶段结束后都会执行、清空,顺序是先清空 process.nextTick 的callback,再执行其他的如Promise 的callback。
常有人误解process.nextTick,会想问例如「一个Tick 是多久?」之类的问题,但其实Tick 指的就是事件回圈中的一个阶段,因此时间是不固定的喔!
结语
今天我们从最基本的介绍出发,认识了Node.js 这个Server 端的JavaScript 执行环境,并提到Node.js 的语言特性在高并发情况的优势,最后重点理解JavaScript 的重要特色:事件回圈,背后是如何在伺服器端执行的。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!