咳咳,各位观众老爷们,大家好!今天咱们来聊聊 Node.js 里一个非常重要,而且非常有趣的东西:EventEmitter! 这货可是 Node.js 事件驱动架构的核心基石,搞明白了它,你才能真正玩转 Node.js 的异步世界。准备好了吗?咱们开始!
第一部分: EventEmitter 是个什么鬼?
要理解 EventEmitter,咱们得先忘掉传统的同步编程思维。在同步编程里,程序一步一步执行,你调用一个函数,它必须执行完,你才能执行下一步。这就像你排队买煎饼果子,必须等前面的人都买完,你才能轮到。
但是,在事件驱动的世界里,程序可以“订阅”一些“事件”,当这些事件发生时,程序才会执行相应的“回调函数”。 这就像你订阅了“煎饼果子出锅”的通知,一旦煎饼果子出锅了,老板就会通知你,你就可以去取你的煎饼果子了,而不用傻傻地排队等着。
EventEmitter 就是一个可以让你创建和管理这些“事件”和“回调函数”的工具。它提供了一种发布/订阅的机制。简单来说,就是:
- 发布 (Emit): EventEmitter 可以“发布”一个事件,告诉大家:“嘿,这个事情发生了!”
- 订阅 (On/addListener): 其他对象可以“订阅”这个事件,告诉 EventEmitter:“如果这个事情发生了,请通知我,并执行我的回调函数!”
- 移除监听 (RemoveListener/Off): 不再需要接收某个事件的通知时,可以取消订阅。
- 只执行一次的订阅 (Once): 订阅某个事件,但回调函数只执行一次。
用人话来说,EventEmitter就像一个八卦中心,大家可以在这里散布消息(发布事件),也可以在这里订阅自己感兴趣的消息(监听事件)。
第二部分: EventEmitter 的基本用法
Node.js 的 events
模块提供了 EventEmitter 类。要使用它,你首先需要 require
这个模块,然后创建一个 EventEmitter 的实例。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {} // 继承 EventEmitter 类
const myEmitter = new MyEmitter();
上面这段代码创建了一个 MyEmitter
类,继承自 EventEmitter。然后,我们创建了一个 myEmitter
实例。现在,我们就可以用它来发布和订阅事件了。
1. 订阅事件 (on/addListener)
on()
和 addListener()
方法用于订阅事件,它们的功能完全一样,只是名字不同而已。 一般我们用 on()
就好了。
myEmitter.on('event', (arg1, arg2) => {
console.log('event occurred!', arg1, arg2);
});
这段代码订阅了名为 event
的事件。当 event
事件发生时,后面的回调函数就会被执行。回调函数可以接收任意数量的参数,这些参数是在发布事件时传递的。
2. 发布事件 (emit)
emit()
方法用于发布事件。
myEmitter.emit('event', 'foo', 'bar'); // 触发 'event' 事件
这段代码发布了 event
事件,并传递了两个参数 'foo'
和 'bar'
。这两个参数会被传递给订阅了 event
事件的回调函数。
合起来看:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (arg1, arg2) => {
console.log('event occurred!', arg1, arg2);
});
myEmitter.emit('event', 'foo', 'bar'); // 输出:event occurred! foo bar
运行这段代码,你会看到控制台输出了 event occurred! foo bar
。
3. 移除监听 (removeListener/off)
removeListener()
和 off()
方法用于移除事件监听器,它们的功能也完全一样,一般用 off()
。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
const listener = (arg1, arg2) => {
console.log('event occurred!', arg1, arg2);
};
myEmitter.on('event', listener);
myEmitter.emit('event', 'foo', 'bar'); // 输出:event occurred! foo bar
myEmitter.off('event', listener); // 移除监听器
myEmitter.emit('event', 'foo', 'bar'); // 不会输出任何东西
重要提示: 要移除监听器,你必须传递 完全相同 的回调函数。 匿名函数是行不通的,因为每次创建都是一个新的匿名函数。
4. 只执行一次的监听 (once)
once()
方法用于订阅事件,但回调函数只会被执行一次。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.once('event', (arg1, arg2) => {
console.log('event occurred!', arg1, arg2);
});
myEmitter.emit('event', 'foo', 'bar'); // 输出:event occurred! foo bar
myEmitter.emit('event', 'foo', 'bar'); // 不会输出任何东西
5. 获取监听器数量 (listenerCount)
listenerCount()
方法用于获取指定事件的监听器数量。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(myEmitter.listenerCount('event')); // 输出:2
6. 获取所有监听器 (listeners)
listeners()
方法用于获取指定事件的所有监听器。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
const listener1 = () => {};
const listener2 = () => {};
myEmitter.on('event', listener1);
myEmitter.on('event', listener2);
const listeners = myEmitter.listeners('event');
console.log(listeners); // 输出:[ [Function: listener1], [Function: listener2] ]
7. 设置最大监听器数量 (setMaxListeners)
EventEmitter 默认对每个事件最多允许 10 个监听器。 如果你超过了这个限制,Node.js 会发出一个警告。 你可以使用 setMaxListeners()
方法来修改这个限制。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.setMaxListeners(20); // 设置最大监听器数量为 20
第三部分:EventEmitter 的错误处理
EventEmitter 有一个特殊的事件叫做 error
。 如果你在 EventEmitter 的实例中抛出一个错误,并且没有监听 error
事件,Node.js 会崩溃。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 没有监听 error 事件,下面的代码会导致 Node.js 崩溃
// myEmitter.emit('error', new Error('Whoops!'));
// 正确的做法是监听 error 事件
myEmitter.on('error', (err) => {
console.error('There was an error:', err);
});
myEmitter.emit('error', new Error('Whoops!')); // 输出:There was an error: Error: Whoops!
所以,记住,一定要监听 error
事件,否则你的程序可能会崩溃!
第四部分:EventEmitter 在事件驱动架构中的作用
EventEmitter 是构建事件驱动架构的基石。事件驱动架构是一种软件架构模式,其中应用程序的组件通过异步事件进行通信。
它有什么好处呢?
- 解耦: 组件之间不需要直接相互调用,它们只需要发布和订阅事件。这使得组件更加独立,更容易维护和修改。
- 异步: 组件之间的通信是异步的,这意味着一个组件不需要等待另一个组件完成任务才能继续执行。这提高了应用程序的性能和响应速度。
- 可扩展性: 你可以很容易地添加新的组件来监听现有的事件,而不需要修改现有的组件。
EventEmitter 在 Node.js 的应用场景非常广泛,例如:
- HTTP 服务器: HTTP 服务器会触发
request
事件,当收到新的 HTTP 请求时。 - 文件系统: 文件系统会触发
change
事件,当文件发生变化时。 - 流 (Stream): 流会触发
data
事件,当有新的数据到达时。
一个简单的 HTTP 服务器例子:
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
console.log('New request received!');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, world!');
});
server.listen(3000, () => {
console.log('Server listening on port 3000');
});
在这个例子中,http.createServer()
返回一个 EventEmitter 实例。 我们使用 server.on('request', ...)
来监听 request
事件。 当收到新的 HTTP 请求时,回调函数就会被执行。
第五部分:EventEmitter 的高级用法 (小彩蛋)
1. 使用 Symbol 作为事件名
你可以使用 Symbol 作为事件名,这样可以避免事件名冲突。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
const myEvent = Symbol('myEvent');
myEmitter.on(myEvent, () => {
console.log('My event occurred!');
});
myEmitter.emit(myEvent); // 输出:My event occurred!
2. 继承 EventEmitter 实现自定义事件
你可以创建自己的类,继承 EventEmitter,并定义自己的事件。
const EventEmitter = require('events');
class Counter extends EventEmitter {
constructor() {
super();
this.count = 0;
}
increment() {
this.count++;
this.emit('count', this.count);
}
}
const counter = new Counter();
counter.on('count', (count) => {
console.log('Count:', count);
});
counter.increment(); // 输出:Count: 1
counter.increment(); // 输出:Count: 2
第六部分:EventEmitter 的坑和注意事项
- 内存泄漏: 如果你创建了很多监听器,并且没有及时移除它们,可能会导致内存泄漏。
- 事件循环阻塞: 如果你的回调函数执行时间过长,可能会阻塞事件循环,导致应用程序响应缓慢。 尽量让回调函数执行时间短。
- this 的指向: 在回调函数中,
this
的指向可能会让你困惑。 可以使用箭头函数或者bind()
方法来改变this
的指向。 - 错误处理: 一定要监听
error
事件,否则你的程序可能会崩溃。 - 同步 vs 异步: EventEmitter 的回调函数是异步执行的。 如果你需要在回调函数中执行同步操作,需要注意顺序问题。
第七部分:总结
特性 | 描述 |
---|---|
on(event, listener) |
订阅事件。当指定事件被触发时,执行监听器函数。 |
emit(event, ...args) |
发布事件。触发与事件关联的所有监听器,并将参数传递给它们。 |
off(event, listener) |
移除事件的监听器。确保提供与添加时完全相同的监听器函数。 |
once(event, listener) |
订阅事件,但监听器函数只执行一次。 |
removeAllListeners([event]) |
移除所有事件的监听器。如果提供了事件名称,则只移除该事件的监听器。 |
setMaxListeners(n) |
设置可以添加到单个 EventEmitter 实例的最大监听器数量。超过此限制会发出警告。 |
getMaxListeners() |
获取当前设置的最大监听器数量。 |
listeners(event) |
返回指定事件的所有监听器函数数组。 |
listenerCount(event) |
返回指定事件的监听器数量。 |
eventNames() |
返回 EventEmitter 实例已经注册的事件名称数组。 |
prependListener(event, listener) |
在监听器数组的开头添加监听器。这确保监听器在其他监听器之前执行。 |
prependOnceListener(event, listener) |
在监听器数组的开头添加一个只执行一次的监听器。 |
rawListeners(event) |
返回指定事件的原始监听器数组,包括通过 once 添加的监听器。 |
总而言之,EventEmitter 是 Node.js 中一个非常强大和灵活的工具。 掌握了它,你就可以构建出更加高效、可扩展和易于维护的应用程序。
好了,今天的 EventEmitter 讲座就到这里了。 希望大家有所收获! 如果有什么问题,欢迎提问。 下课!