接前面
- JavaScript基础学习:ECMAScript 基础知识
- Node.js学习笔记(一):Node.js是什么,如何安装,如何简单使用,工作目录与脚本所在目录获取
- Node.js学习笔记(二):命令行参数、环境变量、标准输入、标准输出、标准出错、信号处理、程序返回值,打包
- Node.js学习笔记(三):fs模块API,三种风格——同步、异步回调、异步promise,三种层级——底层、高层、流
- Node.js学习笔记(四):网络功能:tcp、udp、tls、http、...
- Node.js学习笔记(五):事件模块:EventEmitter、Event、EventTarget、NodeEventTarget
继续了解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,以多次触发监听器:
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()
:
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!
如果多次建立连接,回调会被调用同样次数:
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的话,注意不要用箭头函数:
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