接前面
继续了解Node.js。看看events模块
引言
Nodejs手册中介绍说:
Node.js核心API的很大一部分是基于一种惯用的异步事件驱动架构构建的,其中某些类型的对象(称为“emitters”)会发出命名事件,从而调用函数对象(“listeners”)。
功能类似的三个类:
- EventEmitter,Nodejs中事件发射类
- EventTarget,Nodejs中仿WebAPI接口类
- NodeEventTarget,Nodejs将EventTarget封装成类似EventEmitter的接口
EventEmitter
事件模块的核心类是 EventEmitter,我觉得可以先和Qt信号槽对比一下:
| 特性/概念 |
EventEmitter (Node.js) |
QObject (Qt) |
| 模块 |
内置模块 (events) |
核心库( QtCore ) |
| 实例化 |
const emitter = new EventEmitter(); |
QObject 的派生类的实例化 |
| 添加监听器 |
on() 或 addListener() |
connect() |
| 发射 |
emit() |
调用信号函数,一个空的 emit宏 |
| 一次性监听 |
once() |
connect() 使用Qt::UniqueConnection |
| 移除监听器 |
off()、removeListener() 或 removeAllListeners() |
disconnect() |
| 传递参数 |
通过 emit 方法传递参数给监听器 |
通过信号的参数列表传递数据给槽函数 |
| 自定义事件 |
灵活 |
派生QObject 增加信号 |
通过on()来添加事件监听器,我们在标准输入处理,文件系统模块fs,各种网络模块中的例子中都已经反复在用了。但是只是照猫画虎,需要稍微多了解一点点。
不妨,通过若干简短的例子来学习一下
初识 EventEmitter
建立自定义事件(mySignal)和监听器的连接,而后 emit自定义事件:
| import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.on('mySignal', () => {
console.log('Hi 1+1=10, your event mySignal received!');
});
myEmitter.emit('mySignal');
// output:
// Hi 1+1=10, your event mySignal received!
|
可以多次emit,以多次触发监听器:
1
2
3
4
5
6
7
8
9
10
11
12
13 | import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.on('mySignal', () => {
console.log('Hi 1+1=10, your event mySignal received!');
});
myEmitter.emit('mySignal');
myEmitter.emit('mySignal');
// output:
// Hi 1+1=10, your event mySignal received!
// Hi 1+1=10, your event mySignal received!
|
如果多次emit,只想响应一次,只需要使用once()而不是on():
1
2
3
4
5
6
7
8
9
10
11
12 | import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.once('mySignal', () => {
console.log('Hi 1+1=10, your event mySignal received!');
});
myEmitter.emit('mySignal');
myEmitter.emit('mySignal');
// output:
// Hi 1+1=10, your event mySignal received!
|
如果多次建立连接,回调会被调用同样次数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
const myCallback = () => {
console.log('Hi 1+1=10, your event mySignal received!');
};
myEmitter.on('mySignal', myCallback);
myEmitter.on('mySignal', myCallback);
myEmitter.emit('mySignal');
// output:
// Hi 1+1=10, your event mySignal received!
// Hi 1+1=10, your event mySignal received!
|
传递参数
传递参数简单,直接就是函数参数:
| import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.on('mySignal', (a, b) => {
console.log(`Hi 1+1=10, your event mySignal with ${a} ${b} received!`);
});
myEmitter.emit('mySignal', 1, 2);
// output:
// Hi 1+1=10, your event mySignal with 1 2 received!
|
如果要使用 this的话,注意不要用箭头函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import {EventEmitter} from 'node:events';
const myEmitter = new EventEmitter();
myEmitter.on('mySignal', function (a, b) {
console.log(a, b, myEmitter == this);
});
myEmitter.on('mySignal', (a, b) => {
console.log(a, b, myEmitter == this);
});
myEmitter.emit('mySignal', 1, 2);
// output:
// 1 2 true
// 1 2 false
|
EventTarget 与 Event
Nodejs提供了仿WebAPI中的EventTarget,Event、CustomEvent。EventTarget不是EventEmitter派生类,接口简单:
- eventTarget.addEventListener()
- eventTarget.dispatchEvent()
- eventTarget.removeEventListener()
Node.js 的 EventTarget与Web API的EventTarget 有所不同:
- 在 Node.js 中,没有层次结构或事件传播的概念。分发到
EventTarget 的事件不会自动在嵌套的目标对象层次结构中传播。每个 EventTarget 独立处理其自己的处理程序集。
- 在 Node.js 中,如果事件监听器是异步函数或返回
Promise,并且该 Promise 被拒绝,拒绝将自动捕获并与同步抛出的监听器一样处理。
例子
简单例子:
| const myTarget = new EventTarget();
myTarget.addEventListener('mySignal', (event) => {
console.log(event);
console.log("mySignal received");
});
myTarget.dispatchEvent(new Event('mySignal', {}));
|
输出结果:
| Event {
type: 'mySignal',
defaultPrevented: false,
cancelable: false,
timeStamp: 25.34469997882843
}
mySignal received
|
要传递参数的话,用CustomEvent而不是Event
| const myTarget = new EventTarget();
myTarget.addEventListener('mySignal', (event) => {
console.log(event.detail);
console.log("mySignal received");
});
myTarget.dispatchEvent(new CustomEvent('mySignal', {detail: '1+1=10'}));
//output:
// 1+1=10
// mySignal received
|
所谓CustomEvent,也是标准定义好的。提供了detail成员。
NodeEventTarget
nodejs从EventTarget派生出 NodeEventTarget,以模仿EventEmitter的接口。
但是奇怪,文档少的可怜,我连一个小的demo都凑不齐(跑不通)
| // import NodeEventTarget from 'node-event-target';
const myEmitter = new NodeEventTarget();
myEmitter.on('mySignal', (a, b) => {
console.log(`Hi 1+1=10, your event mySignal with ${a} ${b} received!`);
});
myEmitter.emit('mySignal', 1, 2);
// expected output:
// Hi 1+1=10, your event mySignal with 1 2 received!
|
查看源码,发现其在文件 lib/internal/event_target.js内:
| class NodeEventTarget extends EventTarget {
static [kIsNodeEventTarget] = true;
static defaultMaxListeners = 10;
constructor() {
super();
initNodeEventTarget(this);
}
// ...
|
还是不知道怎么用,以后慢慢看...
参考
- https://nodejs.org/api/events.html
- https://dom.spec.whatwg.org/#event