events 事件系统
NodeJS的事件系统是其异步编程模型的核心基础之一。events模块提供了EventEmitter类,它是NodeJS中实现事件驱动架构的关键组件,许多NodeJS核心模块(如fs、http、stream等)都继承自EventEmitter,这使得事件机制贯穿于整个NodeJS生态系统。
EventEmitter基础
EventEmitter是NodeJS事件系统的核心类,它提供了事件的注册、触发和移除等功能,下面是一个最基本的使用示例。
import { EventEmitter } from "events";
// 创建EventEmitter实例
const emitter = new EventEmitter();
// 注册事件监听器
emitter.on("greet", (name) => {
console.log(`Hello, ${name}!`);
});
// 触发事件
emitter.emit("greet", "NodeJS");
运行结果。
Hello, NodeJS!
代码中,我们首先通过import引入了EventEmitter类,然后创建了一个实例。使用on()方法注册了一个名为greet的事件监听器,最后通过emit()方法触发该事件并传递参数。
注册事件监听器
EventEmitter提供了多种注册事件监听器的方式,以满足不同的使用场景。
on()方法
on()是最常用的事件注册方法,它会将监听器添加到监听器数组的末尾,且可以多次触发。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.on("data", (msg) => {
console.log(`Received: ${msg}`);
});
emitter.emit("data", "First message");
emitter.emit("data", "Second message");
运行结果如下。
Received: First message
Received: Second message
once()方法
once()方法注册的监听器只会触发一次,触发后自动移除。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.once("connect", () => {
console.log("Connected!");
});
emitter.emit("connect"); // 输出: Connected!
emitter.emit("connect"); // 不会触发,监听器已被移除
prependListener()方法
prependListener()方法将监听器添加到监听器数组的开头,使其优先执行。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.on("message", () => {
console.log("Listener 1");
});
emitter.prependListener("message", () => {
console.log("Listener 2 (prepended)");
});
emitter.emit("message");
运行结果如下。
Listener 2 (prepended)
Listener 1
移除事件监听器
在某些场景下,我们需要移除已注册的事件监听器,以避免内存泄漏或重复执行。
off()方法
off()方法用于移除指定的事件监听器,注意必须传入与注册时相同的函数引用。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
const handler = (msg) => {
console.log(`Message: ${msg}`);
};
emitter.on("message", handler);
emitter.emit("message", "Hello"); // 输出: Message: Hello
emitter.off("message", handler);
emitter.emit("message", "World"); // 无输出,监听器已被移除
removeAllListeners()方法
removeAllListeners()方法用于移除指定事件的所有监听器,或移除所有事件的所有监听器。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.on("event1", () => console.log("Event 1 - Handler 1"));
emitter.on("event1", () => console.log("Event 1 - Handler 2"));
emitter.on("event2", () => console.log("Event 2 - Handler 1"));
// 移除event1的所有监听器
emitter.removeAllListeners("event1");
emitter.emit("event1"); // 无输出
emitter.emit("event2"); // 输出: Event 2 - Handler 1
// 移除所有事件的所有监听器
emitter.removeAllListeners();
向监听器传递参数
emit()方法支持向监听器传递参数。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.on("userInfo", (name, age, email) => {
console.log(`Name: ${name}`);
console.log(`Age: ${age}`);
console.log(`Email: ${email}`);
});
emitter.emit("userInfo", "Tom", 18, "tom@example.com");
运行结果如下。
Name: Tom
Age: 18
Email: tom@example.com
监听器数量限制
默认情况下,每个事件最多可以注册10个监听器。超过这个限制时,NodeJS会输出警告信息(但不会阻止添加)。我们可以通过setMaxListeners()方法修改这个限制。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
// 查看当前最大监听器数量
console.log(emitter.getMaxListeners()); // 输出: 10
// 修改最大监听器数量
emitter.setMaxListeners(20);
console.log(emitter.getMaxListeners()); // 输出: 20
// 设置为0或Infinity表示不限制数量
emitter.setMaxListeners(0);
获取事件和监听器信息
EventEmitter提供了一些方法用于获取当前实例的事件和监听器信息。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
const handler1 = () => console.log("Handler 1");
const handler2 = () => console.log("Handler 2");
emitter.on("event1", handler1);
emitter.on("event1", handler2);
emitter.on("event2", handler1);
// 获取所有已注册的事件名称
console.log(emitter.eventNames()); // 输出: [ 'event1', 'event2' ]
// 获取指定事件的监听器数量
console.log(emitter.listenerCount("event1")); // 输出: 2
// 获取指定事件的监听器数组
console.log(emitter.listeners("event1")); // 输出: [ [Function: handler1], [Function: handler2] ]
内置事件
EventEmitter本身会触发一些内置事件,我们可以监听这些事件来追踪监听器的变化。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
// 当添加新的监听器时触发
emitter.on("newListener", (eventName, listener) => {
console.log(`New listener added for: ${eventName}`);
});
// 当移除监听器时触发
emitter.on("removeListener", (eventName, listener) => {
console.log(`Listener removed for: ${eventName}`);
});
const handler = () => console.log("Hello");
emitter.on("greet", handler); // 触发newListener
emitter.off("greet", handler); // 触发removeListener
运行结果如下。
New listener added for: greet
Listener removed for: greet
错误事件处理
在NodeJS中,error事件是一个特殊的事件,如果EventEmitter触发了error事件但没有注册对应的监听器,NodeJS会抛出异常并终止进程,我们可以为error事件注册监听器,拦截这一过程。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
// 注册error事件监听器
emitter.on("error", (err) => {
console.error(`Error occurred: ${err.message}`);
});
// 触发error事件
emitter.emit("error", new Error("Something went wrong"));
运行结果如下。
Error occurred: Something went wrong
如果不注册error事件监听器会导致程序崩溃。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
// 未注册error监听器,触发error事件会导致程序崩溃
emitter.emit("error", new Error("Unhandled error"));
// 抛出异常: Error: Unhandled error
继承EventEmitter
在实际开发中,我们可以创建自定义类并继承EventEmitter,这样我们的类就具备了事件能力。
import { EventEmitter } from "events";
class DataStream extends EventEmitter {
constructor() {
super();
this.data = [];
}
addData(item) {
this.data.push(item);
this.emit("dataAdded", item);
if (this.data.length >= 5) {
this.emit("bufferFull", this.data);
}
}
clear() {
this.data = [];
this.emit("cleared");
}
}
const stream = new DataStream();
stream.on("dataAdded", (item) => {
console.log(`Data added: ${item}`);
});
stream.on("bufferFull", (data) => {
console.log(`Buffer is full: [${data.join(", ")}]`);
});
stream.on("cleared", () => {
console.log("Data cleared");
});
stream.addData("A");
stream.addData("B");
stream.addData("C");
stream.addData("D");
stream.addData("E");
stream.clear();
运行结果如下。
Data added: A
Data added: B
Data added: C
Data added: D
Data added: E
Buffer is full: [A, B, C, D, E]
Data cleared
异步事件处理
EventEmitter的事件触发是同步的,所有监听器会按照注册顺序依次执行,不过监听器中可以使用异步函数作为处理逻辑。
import { EventEmitter } from "events";
const emitter = new EventEmitter();
emitter.on("asyncTask", async (taskId) => {
console.log(`Task ${taskId} started`);
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(`Task ${taskId} completed`);
});
console.log("Before emit");
emitter.emit("asyncTask", 1);
console.log("After emit");
运行结果如下。
Before emit
Task 1 started
After emit
Task 1 completed
注意观察输出顺序,emit()本身是同步返回的,但监听器内部的异步操作会在后续执行。
使用Promise包装事件
events提供了once()方法可以将事件转换为Promise,方便在异步代码中使用。
import { EventEmitter, once } from "events";
const emitter = new EventEmitter();
const waitForEvent = async () => {
console.log("Waiting for event...");
// 1秒后触发事件
setTimeout(() => {
emitter.emit("ready", "Data is ready");
}, 1000);
// 等待事件触发
const [result] = await once(emitter, "ready");
console.log(`Received: ${result}`);
};
waitForEvent();
运行结果如下。
Waiting for event...
Received: Data is ready