Node.js 事件循环机制与回调函数详解
本文深入解析Node.js的单线程事件循环机制,阐述其如何通过观察者模式实现高并发。详细对比阻塞式与非阻塞式I/O操作,结合fs模块的同步与异步API示例,说明回调函数在异步编程中的核心作用及编码注意事项。
本文深入解析Node.js的事件循环机制与回调函数原理。
Node.js 事件循环
一、Node.js 事件循环
Node.js采用单进程单线程架构,但借助事件与回调机制支持高并发,因此具备极高的性能表现。Node.js的每个API均为异步执行,并作为独立线程运行,通过异步函数调用处理并发任务。其事件机制主要基于设计模式中的观察者模式实现。Node.js的主线程类似于一个持续运行的while(true)事件循环,只要存在未退出的事件观察者,循环便不会结束。每个异步事件都会生成一个对应的事件观察者,一旦事件发生,系统即调用相应的回调函数。
1、事件驱动程序
Node.js采用事件驱动模型。当Web服务器接收到请求时,会立即关闭该连接并处理请求,随后去服务下一个Web请求。当原请求处理完成,结果被放回处理队列,待到达队列头部时,结果返回给用户。该模型具备极高的效率与可扩展性,因为Web服务器持续接收请求而不等待任何读写操作完成(即非阻塞式IO或事件驱动IO)。在事件驱动模型中,系统生成主循环监听事件,检测到事件时触发回调函数。
整个事件驱动流程实现简洁,类似于观察者模式:事件相当于主题(Subject),而注册到该事件上的所有处理函数相当于观察者(Observer)。Node.js内置多个事件,开发者可通过引入events模块,实例化EventEmitter类来绑定和监听事件,如下实例所示:
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
以下程序绑定事件处理程序:
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);
我们可以通过程序触发事件:
// 触发事件
eventEmitter.emit('eventName');
2、实例
创建main.js文件,代码如下所示:
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 创建事件处理程序
var connectHandler = function connected() {
console.log('连接成功。');
// 触发 data_received 事件
eventEmitter.emit('data_received');
}
// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('数据接收成功。');
});
// 触发 connection 事件
eventEmitter.emit('connection');
console.log("程序执行完毕。");

Node.js 回调函数
二、Node.js 回调函数
Node.js中异步编程的直接体现即为回调。异步编程依托回调实现,但使用回调并不意味着程序自动变为异步。回调函数在任务完成后被调用,Node.js大量使用回调函数,其所有API均支持回调。例如,读取文件时可同时执行其他命令,文件读取完成后,文件内容作为参数传入回调函数。这种方式在执行代码时无需阻塞或等待文件I/O操作,从而大幅提升Node.js性能,使其能处理大量并发请求。
1、阻塞代码实例
创建一个文件test.txt,内容如下:
Hello World!
fs.readFileSync()
fs.readFile()
创建test.js文件,代码如下:
console.log('-------程序开始执行--------');
// 引入fs模块
var fs = require("fs");
//同步读取文件
var data = fs.readFileSync('test.txt','utf-8');
console.log(data.toString());
console.log('-------程序执行结束--------');
以上代码执行结果如下:
2、非阻塞代码实例
创建test.js文件,代码如下:
console.log('-------程序开始执行--------');
// 引入fs模块
var fs = require("fs");
//异步读取文件
fs.readFile('test.txt','utf-8',function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log('-------程序执行结束--------');
上述程序中,fs.readFile()为异步函数,用于读取文件。若读取过程中发生错误,错误信息将通过err对象输出;若无错误,readFile跳过err对象的输出,文件内容通过回调函数返回。
以上代码执行结果如下:
接下来删除input.txt文件,执行结果如下所示:
通过上述两个实例,我们了解了阻塞与非阻塞调用的区别。第一个实例在文件读取完成后才结束程序;第二个实例无需等待文件读取完成,即可同时执行后续代码,从而大幅提升程序性能。因此,阻塞调用按顺序执行,而非阻塞调用无需按顺序执行。若需处理回调函数的参数,必须将逻辑写在回调函数内部。
fs.readFileSync和fs.readFile
三、fs.readFileSync和fs.readFile
1、s.readFileSync
语法:fs.readFileSync(filename, [encoding])
接收参数:
filename:文件路径
options:option对象,包含encoding编码格式,该项为可选参数。
由于Node.js仅支持utf8、ucs2、ascii、binary、base64、hex等编码格式,不支持中文GBK或GB2312等编码。因此,若要读写GBK或GB2312格式文件的中文内容,必须使用额外模块iconv-lite。
2、fs.readFile
语法:fs.readFile(filename, [encoding], [callback(err,data)])
接收参数:
filename:文件路径
options:option对象,包含encoding编码格式,该项为可选参数。
callback:回调函数,传递两个参数:异常err和文件内容data。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
macOS卸载Node.js指南:区分Homebrew与pkg安装包的处理方式
2026-11-05 11:28:12
JS逆向入门:浏览器Console调试与Node.js环境搭建指南
2026-11-05 10:57:15
Node.js 大版本升级需要注意哪些破坏性变更
2026-11-03 20:35:44
Node.js官网LTS版与Current版有什么区别
2026-11-03 19:33:49
Windows彻底卸载Node.js:清理AppData残留与PATH环境变量
2026-11-03 19:15:35
