Node.js原理:阻塞、EventEmitter及其继承运用实战

作者:夜航船时间:2026-10-25 18:59:29浏览:392

本文深入解析Node.js的核心原理,重点阐述阻塞与非阻塞代码的执行差异,以及基于回调函数的异步编程模式。通过实战代码演示EventEmitter类的创建、事件绑定与触发机制,并探讨如何在自定义对象中继承Event以构建支持事件响应的核心模块。

基本概念

简单来说,Node.js 是一种在服务器端运行的 JavaScript 运行环境。

$ node

> console.log('Hello 黎燃!');
Hello 黎燃!

然而,Node.js 的概念与传统的 JavaScript 有着本质的不同。

在使用 Node.js 时,我们不仅是在开发应用程序,实际上也是在构建整个 HTTP 服务器。

事实上,我们的 Web 应用程序与对应的 Web 服务器在结构上基本一致。让我们了解一下 Node.js 应用程序的组成部分: 1. 加载所需模块:我们可以使用 require 命令来加载 Node.js 模块。 2. 创建服务器:服务器可以监听客户端的请求,其功能类似于 Apache 和 Nginx 等 HTTP 服务器。 3. 接收并响应请求:服务器很容易创建。客户端可以通过浏览器或终端发送 HTTP 请求,服务器接收到请求后会返回相应的数据。

回调函数

Node.js 中异步编程的直接体现就是回调机制。

阻塞代码

异步编程依赖于回调,但这并不意味着使用了回调函数程序就一定是异步的。任务完成后会调用回调函数,Node.js 中大量使用了回调函数,且其所有 API 都支持回调机制。

在读取文件后,我们将文件内容作为回调函数的参数返回。这样,在执行后续代码时,就不会阻塞或等待文件 I/O 操作的完成。

回调函数通常作为函数的最后一个参数出现:

function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

创建主 JS 文件,代码如下:

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("程序执行结束!");

非阻塞代码

创建主 JS 文件,代码如下:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});

console.log("程序执行结束!");

通过上述两个示例,我们可以理解阻塞调用与非阻塞调用的区别。第一个实例在读取文件后才会执行后续程序;而在第二个示例中,我们无需等待文件读取完成,可以在读取文件的同时执行后续代码,这大大提高了程序的性能。

因此,阻塞代码需要按顺序执行,而非阻塞代码则不需要严格遵循顺序。

Node.js 事件循环

基本概念

Node.js 采用事件驱动模型。当 Web 服务器接收到一个请求时,它会处理该请求,然后为下一个 Web 请求提供服务。当请求处理完成时,它会被放回处理队列。当它到达队列头部时,结果将返回给用户。接下来我们引入 events 模块。

var events = require('events');

创建 eventEmitter 对象

var eventEmitter = new events.EventEmitter();

绑定事件及事件的处理程序

eventEmitter.on('eventName', eventHandler);

触发事件

eventEmitter.emit('eventName');

Node 应用程序工作原理

创建主 JS 文件,代码如下:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序执行完毕");

在上述程序中,fs.readFile() 是一个用于读取文件的异步函数。如果在读取文件期间发生错误,error err 对象将输出错误消息。

程序执行完毕

如果没有发生错误,readfile 将跳过 err 对象的输出,并通过回调函数输出文件内容。

接下来,我们删除输入 txt 文件。

执行结果如下:

程序执行完毕 Error: ENOENT, open 'input.txt'

EventEmitter 类

每次有新连接时,服务器对象的 net 将触发事件,而 readstream 对象的 fs 将在文件打开时触发事件。所有生成事件的对象都是 EventEmitter 实例中的事件。

引入 events 模块

var events = require('events');

创建 eventEmitter 对象

var eventEmitter = new events.EventEmitter();

如果在实例化 EventEmitter 对象期间发生错误,将触发 error 事件。添加新监听器时,将触发 newListener 事件。删除监听器时,将触发 removeListener 事件。

var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
    console.log('some_event 事件触发'); 
}); 
setTimeout(function() { 
    event.emit('some_event'); 
}, 1000); 

运行此代码,控制台在 1 秒后输出 'some'。触发事件时,将依次调用注册到此事件的事件监听器,并将事件参数作为回调函数参数传递。

var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 

on(event, listener)

注册指定事件的监听器,接受字符串事件名和回调函数。

server.on('connection', function (stream) {
  console.log('someone connected!');
});

继承 EventEmitter

大多数时候,我们不直接使用 EventEmitter,而是在对象中继承它。所有支持事件响应的核心模块,包括 fs、net 和 http,都是 EventEmitter 的子类。

  • 1. 首先,具有实体函数的对象实现了符合语义的事件。事件的监听和发生应该是对象的一种方法。
  • 2. 其次,JavaScript 的对象机制是基于原型的,支持部分多重继承。继承 EventEmitter 不会干扰对象的原始继承关系。

到此这篇关于 Node.js 原理对于阻塞和 EventEmitter 及其继承的运用实战的文章就介绍到这了,更多相关 Node.js EventEmitter 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章