nodejs执行javaScript原理超详细讲解
作者:zhousenshan
前言
Node.js 执行 JavaScript 的过程可以分解为一个高效、事件驱动的运行模型。它与浏览器中的 JavaScript 引擎有相似的核心,但有不同的全局环境和能力。
简单来说,Node.js 使用 V8 引擎 来执行 JavaScript 代码,并通过 事件循环(Event Loop) 来处理异步操作。
下面我们来详细拆解这个过程。
核心架构:不只是 V8
Node.js 的运行时环境主要由以下几个部分组成:
V8 引擎:来自 Google,负责编译和执行 JavaScript 代码。它将 JS 代码转换成更快的机器码,而不是一行行地解释执行。
LibUV:一个用 C 编写的库,它提供了 事件循环(Event Loop)、线程池 和异步 I/O 操作(如文件系统、网络操作)的能力。这是 Node.js 实现非阻塞 I/O 的关键。
其他底层库:如
http-parser、c-ares、OpenSSL等,分别用于处理 HTTP、DNS 和加密等。
执行流程详解
当你运行 node app.js 时,会发生以下步骤:
第 1 步:初始化环境
Node.js 启动,初始化 V8 引擎、LibUV 等组件。
创建事件循环。
设置全局对象(如
global、process)、模块加载系统等。
第 2 步:加载并执行你的代码
Node.js 读取你的
app.js文件。V8 引擎开始处理这段代码。这个过程包括:
解析:将 JS 代码转换成抽象语法树(AST)。
编译:V8 的即时编译(JIT)引擎会将 AST 编译成高效的机器码。
执行:CPU 执行生成的机器码。
第 3 步:处理同步与异步操作(关键阶段)
这是 Node.js 并发模型的核心。
同步任务:会立即在主线程上按顺序执行,这与在浏览器中一样。
异步任务(如
fs.readFile、setTimeout、http.request):当遇到异步操作时,Node.js 会立即返回,继续执行后面的同步代码,不会“阻塞”在这里。
同时,它会将这个异步操作及其回调函数 交给 LibUV 去处理。
第 4 步:LibUV 与事件循环的工作
LibUV 通过事件循环来协调这些异步操作。事件循环是一个无限循环,它不断地检查是否有待处理的事件。其简化模型如下:
┌───────────────────────────┐
┌─>│ 定时器 │ (执行到点的 setTimeout、setInterval 回调)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ (执行大部分系统操作的回调,如 TCP 错误)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ (内部使用)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ 轮询 poll │ ◄─── 最重要的阶段
│ └─────────────┬─────────────┘ - 检索新的 I/O 事件
│ ┌─────────────┴─────────────┐ - 执行与 I/O 相关的回调
│ │ 检查 check │ (执行 setImmediate 的回调)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ 关闭事件回调 close │ (执行关闭事件的回调,如 socket.on('close', ...))
└───────────────────────────┘
不同类型的异步任务被放入不同的队列:
定时器:
setTimeout和setInterval的回调。I/O 操作 & 轮询:文件读写、网络请求等。这是最繁忙的阶段。
检查阶段:
setImmediate的回调。微任务:
Promise.then()、process.nextTick()。注意:微任务在每个阶段之间都会优先清空,这意味着process.nextTick和Promise的回调总是比上面提到的其他异步回调执行得更早。
第 5 步:执行回调
当异步操作完成时(例如,文件读取完毕或定时器超时),LibUV 会将对应的回调函数放入相应的队列中。事件循环在到达对应阶段时,会从队列中取出回调函数,送到 V8 引擎中去执行。
一个简单的例子
// 同步代码
console.log('Script start'); // 1. 立即执行
// 异步代码 - 定时器
setTimeout(() => {
console.log('setTimeout'); // 4. 在定时器阶段执行
}, 0);
// 异步代码 - Promise (微任务)
Promise.resolve().then(() => {
console.log('Promise'); // 3. 在同步代码执行后,事件循环下一个阶段前执行
});
// 同步代码
console.log('Script end'); // 2. 立即执行输出顺序为:
Script start
Script end
Promise
setTimeout
总结:Node.js 如何执行 JS
核心引擎:使用 V8 来编译和执 JavaScript 代码。
非阻塞 I/O:通过 LibUV 来处理所有异步操作,使其不阻塞主线程。
事件驱动:事件循环 是中枢,它不断地轮询,检查已完成的事件,然后安排对应的回调函数执行。
单线程但高效:你的 JavaScript 代码(回调)是在单个主线程上运行的,但所有的 I/O 操作都是通过 LibUV 在底层多线程或系统内核的帮助下异步完成的。这使得 Node.js 可以用一个线程处理成千上万的并发连接。
这种模型非常适合 I/O 密集型的应用(如 Web 服务器、数据库工具),因为大部分时间都在等待 I/O,而不是进行大量计算。
到此这篇关于nodejs执行javaScript原理的文章就介绍到这了,更多相关nodejs执行JS原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
