Node.js 事件循环机制与回调函数详解

作者:墨池

时间:2026-10-30

浏览:216

本文深入解析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)。在事件驱动模型中,系统生成主循环监听事件,检测到事件时触发回调函数。Node.js 事件循环

整个事件驱动流程实现简洁,类似于观察者模式:事件相当于主题(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中异步编程的直接体现即为回调。异步编程依托回调实现,但使用回调并不意味着程序自动变为异步。回调函数在任务完成后被调用,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('-------程序执行结束--------'); 

以上代码执行结果如下:Node.js 事件循环

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对象的输出,文件内容通过回调函数返回。

以上代码执行结果如下:Node.js 事件循环 接下来删除input.txt文件,执行结果如下所示:Node.js 事件循环 通过上述两个实例,我们了解了阻塞与非阻塞调用的区别。第一个实例在文件读取完成后才结束程序;第二个实例无需等待文件读取完成,即可同时执行后续代码,从而大幅提升程序性能。因此,阻塞调用按顺序执行,而非阻塞调用无需按顺序执行。若需处理回调函数的参数,必须将逻辑写在回调函数内部。

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。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章