文章目录
- 简言
- Events
- Passing arguments and this to listeners 向监听器传递参数
- Asynchronous vs. synchronous 异步和同步
- Handling events only once 只一次处理事件
- Error events 错误事件
- Capture rejections of promises 捕捉拒绝承诺的情况
- Class: EventEmitter 事件类
- Event: 'newListener' 新监听事件
- Event: 'removeListener' 移除监听事件
- emitter.addListener(eventName, listener) 添加监听器
- emitter.emit(eventName[, ...args]) 触发监听器
- emitter.eventNames() 获取已注册监听器名称列表
- emitter.getMaxListeners() 获取最大监听器值
- emitter.listenerCount(eventName[, listener]) 获取指定监听器数量
- emitter.listeners(eventName) 获取指定监听器列表
- emitter.off(eventName, listener) 移除监听
- emitter.on(eventName, listener) 注册监听器
- emitter.once(eventName, listener) 注册一次性监听器
- emitter.prependListener(eventName, listener) 前面添加监听器
- emitter.prependOnceListener(eventName, listener) 前面添加一次性监听器
- emitter.removeAllListeners([eventName]) 移除指定名称的所有监听器
- emitter.removeListener(eventName, listener) 移除指定名称的监听器
- emitter.setMaxListeners(n) 设置最大监听器值
- emitter.rawListeners(eventName) 获取指定侦听器副本
- emitter[Symbol.for('nodejs.rejection')](err, eventName[, ...args]) 拒绝承诺函数
- events.defaultMaxListeners 默认最大监听器值
- events.errorMonitor 监听错误监听器
- events.getEventListeners(emitterOrTarget, eventName) 获取指定事件监听器数组副本
- events.getMaxListeners(emitterOrTarget) 获取当前监听器最大数量
- events.once(emitter, name[, options]) 创建监听emitter错误的promise
- Awaiting multiple events emitted on process.nextTick() 等待 process.nextTick() 发出的多个事件
- events.captureRejections 全局 captureRejections 选项
- events.captureRejectionSymbol
- events.listenerCount(emitter, eventName)
- events.on(emitter, eventName[, options]) 用于迭代由发射器发射的 eventName 事件
- events.setMaxListeners(n[, ...eventTargets]) 设置最大监听器数量
- Class: events.EventEmitterAsyncResource extends EventEmitter
- new events.EventEmitterAsyncResource([options]) 创建EventEmitterAsyncResource实例
- eventemitterasyncresource.asyncId 分配给资源的唯一 asyncId。
- eventemitterasyncresource.asyncResource 底层 \<AsyncResource>。
- eventemitterasyncresource.emitDestroy() 销毁事件
- eventemitterasyncresource.triggerAsyncId
- EventTarget and Event API
- Node.js EventTarget vs. DOM EventTarget
- NodeEventTarget 比较 EventEmitter
- Event listener 事件监听器
- EventTarget error handling EventTarget 错误处理
- Class: Event Event类
- event.bubbles
- event.cancelBubble
- event.cancelable
- event.composed
- event.composedPath()
- event.currentTarget
- event.defaultPrevented
- event.eventPhase
- event.initEvent(type[, bubbles[, cancelable]])
- event.isTrusted
- event.preventDefault()
- event.returnValue
- event.srcElement
- event.stopImmediatePropagation()
- event.stopPropagation()
- event.target
- event.timeStamp
- event.type
- Class: EventTarget
- eventTarget.addEventListener(type, listener[, options])
- eventTarget.dispatchEvent(event)
- eventTarget.removeEventListener(type, listener[, options])
- Class: CustomEvent
- event.detail
- Class: NodeEventTarget
- nodeEventTarget.addListener(type, listener) 添加
- nodeEventTarget.emit(type, arg) 触发
- nodeEventTarget.eventNames()
- nodeEventTarget.listenerCount(type)
- nodeEventTarget.setMaxListeners(n)
- nodeEventTarget.getMaxListeners()
- nodeEventTarget.off(type, listener[, options])
- nodeEventTarget.on(type, listener)
- nodeEventTarget.once(type, listener)
- nodeEventTarget.removeAllListeners([type])
- nodeEventTarget.removeListener(type, listener[, options])
- 结语
简言
Node.js 的大部分核心 API 都是围绕一种惯用的异步事件驱动架构构建的,在这种架构中,某些类型的对象(称为 “发射器”)会发射命名事件,从而导致函数对象(称为 “监听器”)被调用。
当我们学会events模块后,就可以自定义事件监听和触发时机了。
Events
Node.js 的大部分核心 API 都是围绕一种惯用的异步事件驱动架构构建的,在这种架构中,某些类型的对象(称为 “发射器”)会发射命名事件,从而导致函数对象(称为 “监听器”)被调用。
例如:net.Server 对象会在每次有对等设备连接到它时发出一个事件;fs.ReadStream 会在文件打开时发出一个事件;数据流会在有数据可读取时发出一个事件。
所有发射事件的对象都是 EventEmitter 类的实例。这些对象会暴露一个 eventEmitter.on() 函数,该函数允许将一个或多个函数附加到对象发出的已命名事件上。通常情况下,事件名称是驼峰字符串,但也可以使用任何有效的 JavaScript 属性键。
当 EventEmitter 对象发出一个事件时,所有与该特定事件相关的函数都会被同步调用。被调用的监听器返回的任何值都将被忽略和丢弃。
下面的示例显示了一个带有单个监听器的简单 EventEmitter 实例。eventEmitter.on() 方法用于注册监听器,而 eventEmitter.emit() 方法用于触发事件。
import { EventEmitter } from 'node:events';class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();
myEmitter.on('event', () => {console.log('an event occurred!');
});
myEmitter.emit('event');
Passing arguments and this to listeners 向监听器传递参数
eventEmitter.emit() 方法允许将任意一组参数传递给监听器函数。请记住,在调用普通监听器函数时,标准 this 关键字会被有意设置为引用监听器所连接的 EventEmitter 实例。
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter);// Prints:// a b MyEmitter {// _events: [Object: null prototype] { event: [Function (anonymous)] },// _eventsCount: 1,// _maxListeners: undefined,// [Symbol(shapeMode)]: false,// [Symbol(kCapture)]: false// } true
});
myEmitter.emit('event', 'a', 'b');
可以使用 ES6 箭头函数作为监听器,但这样做时,this 关键字将不再引用 EventEmitter 实例:
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {console.log(a, b, this);// Prints: a b undefined
});
myEmitter.emit('event', 'a', 'b');
Asynchronous vs. synchronous 异步和同步
**EventEmitter 会按照注册的顺序同步调用所有监听器。**这可确保事件的正确排序,并有助于避免竞赛条件和逻辑错误。在适当的时候,监听器函数可以使用 setImmediate() 或 process.nextTick() 方法切换到异步操作模式:
import { EventEmitter } from "node:events";class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on("event", (a, b) => {setImmediate(() => {console.log("this happens asynchronously");});console.log(a, b);
});
myEmitter.emit("event", "a", "b");
Handling events only once 只一次处理事件
如果使用 eventEmitter.on() 方法注册了监听器,那么每次指定的事件发生时,都会调用该监听器。
使用 eventEmitter.once() 方法可以注册一个监听器,该监听器最多只能被调用一次。一旦事件发生,监听器就会被取消注册,然后再被调用。
let m = 0;
myEmitter.once("event", () => {console.log(++m);
});
myEmitter.emit("event");
// Prints: 1
myEmitter.emit("event");
// Ignored 忽略
Error events 错误事件
当 EventEmitter 实例发生错误时,典型的操作是发出一个 "error"事件。这些在 Node.js 中被视为特殊情况。
如果一个 EventEmitter 没有为 "error"事件注册至少一个监听器,并且发出了 "错误 "事件,则会抛出错误、打印堆栈跟踪并退出 Node.js 进程。
为了防止 Node.js 进程崩溃,可以使用 domain 模块。(但请注意,node:domain 模块已被弃用)。
作为最佳实践,应始终为 "error"事件添加监听器。
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
通过使用符号 events.errorMonitor 安装一个监听器,可以监听 "错误 "事件,而无需消耗所发出的错误。
import { EventEmitter, errorMonitor } from 'node:events';const myEmitter = new EventEmitter();
myEmitter.on(errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('whoops!'));
// 触发错误事件监听,仍会抛出 Node.js 并导致其崩溃
Capture rejections of promises 捕捉拒绝承诺的情况
在事件处理程序中使用异步函数是有问题的,因为在抛出异常的情况下,它会导致未处理的拒绝:
import { EventEmitter } from 'node:events';
const ee = new EventEmitter();
ee.on('something', async (value) => {throw new Error('kaboom');
});
EventEmitter 构造函数中的 captureRejections 选项或全局设置会改变这种行为,在 Promise 上安装 .then(undefined, handler) 处理程序。
const ee = new EventEmitter({ captureRejections: true });
ee.on("something", async (value) => {throw new Error("kaboom");
});
ee.on("error", console.log);
ee[Symbol.for("nodejs.rejection")] = () => {console.log("捕捉到了");
};ee.emit("something");
设置 events.captureRejections = true 将更改 EventEmitter 所有新实例的默认值。
import { EventEmitter } from 'node:events';EventEmitter.captureRejections = true;
const ee1 = new EventEmitter();
ee1.on('something', async (value) => {throw new Error('kaboom');
});ee1.on('error', console.log);
由 captureRejections 行为生成的 "错误 "事件没有 catch 处理程序,以避免无限错误循环:建议不要使用 async 函数作为 "错误 "事件处理程序。
Class: EventEmitter 事件类
EventEmitter 类可以用于创建事件监听和事件触发实例,由 node:events 模块定义并公开:
import { EventEmitter } from 'node:events';
当添加新的侦听器时,所有 EventEmitters 都会发出 "newListener "事件;当移除现有侦听器时,所有 EventEmitters 都会发出 "removeListener "事件。
它支持以下选项:
- captureRejections <boolean>启用自动捕捉承诺拒绝。默认值:false。
Event: ‘newListener’ 新监听事件
参数:
- eventName —— 正在监听的事件名称。
- listener —— 事件处理函数。
在监听器被添加到其内部的监听器数组之前,EventEmitter 实例会发出自己的 "newListener "事件。
为 "newListener "事件注册的监听器会收到事件名称和被添加监听器的引用。
事实上,在添加监听器之前触发事件有一个微妙但重要的副作用:在 "newListener "回调中以相同名称注册的任何其他监听器都会被插入到正在添加的监听器之前。
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();
// Only do this once so we don't loop forever
myEmitter.once('newListener', (event, listener) => {if (event === 'event') {// Insert a new listener in frontmyEmitter.on('event', () => {console.log('B');});}
});
myEmitter.on('event', () => {console.log('A');
});
myEmitter.emit('event');
// Prints:
// B
// A
Event: ‘removeListener’ 移除监听事件
参数:
- eventName <string> | <symbol> 事件名称
- listener <函数> 事件处理函数
移除监听器后会发出 "removeListener "事件。
emitter.addListener(eventName, listener) 添加监听器
参数:
- eventName <字符串> | <符号> 事件名称
- listener <Function> 事件处理函数
emitter.on(eventName, listener) 的别名。
emitter.emit(eventName[, …args]) 触发监听器
同步调用为名为 eventName 的事件注册的每个监听器,调用顺序与注册顺序一致,并将提供的参数传递给每个监听器。
如果事件有监听者,则返回 true,否则返回 false。
import { EventEmitter } from 'node:events';
const myEmitter = new EventEmitter();// First listener
myEmitter.on('event', function firstListener() {console.log('Helloooo! first listener');
});
// Second listener
myEmitter.on('event', function secondListener(arg1, arg2) {console.log(`event with parameters ${arg1}, ${arg2} in second listener`);
});
// Third listener
myEmitter.on('event', function thirdListener(...args) {const parameters = args.join(', ');console.log(`event with parameters ${parameters} in third listener`);
});console.log(myEmitter.listeners('event'));myEmitter.emit('event', 1, 2, 3, 4, 5);// Prints:
// [
// [Function: firstListener],
// [Function: secondListener],
// [Function: thirdListener]
// ]
// Helloooo! first listener
// event with parameters 1, 2 in second listener
// event with parameters 1, 2, 3, 4, 5 in third listener
emitter.eventNames() 获取已注册监听器名称列表
返回一个数组,其中列出发射器已注册监听器的事件。数组中的值为字符串或符号。
import { EventEmitter } from 'node:events';const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});const sym = Symbol('symbol');
myEE.on(sym, () => {});console.log(myEE.eventNames());
// Prints: [ 'foo', 'bar', Symbol(symbol) ]
emitter.getMaxListeners() 获取最大监听器值
返回事件发射器的当前最大监听器值,该值由 emitter.setMaxListeners(n) 设置或默认为 events.defaultMaxListeners。
emitter.listenerCount(eventName[, listener]) 获取指定监听器数量
返回监听名为 eventName 的事件的监听者数量。如果提供了监听器,则将返回监听器在事件监听器列表中被找到的次数。
emitter.listeners(eventName) 获取指定监听器列表
返回名为 eventName 的事件的侦听器数组副本。
emitter.off(eventName, listener) 移除监听
emitter.removeListener() 的别名。
emitter.on(eventName, listener) 注册监听器
将监听器函数添加到名为 eventName 的事件的监听器数组末尾。不会检查是否已添加监听器。多次调用事件名和监听器的相同组合将导致监听器被多次添加和调用。
server.on('connection', (stream) => {console.log('someone connected!');
});
返回对 EventEmitter 的引用,以便进行链式调用。
默认情况下,事件监听器会按照添加的顺序被调用。可以使用 emitter.prependListener() 方法将事件监听器添加到监听器数组的开头。
import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
emitter.once(eventName, listener) 注册一次性监听器
为名为 eventName 的事件添加一次性监听器函数。下次事件名称被触发时,该监听器将被移除,然后再被调用。
返回对 EventEmitter 的引用,以便进行链式调用。
默认情况下,事件监听器会按照添加的顺序被调用。可以使用 emitter.prependOnceListener() 方法将事件监听器添加到监听器数组的开头。
import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
emitter.prependListener(eventName, listener) 前面添加监听器
将监听器函数添加到名为 eventName 的事件的监听器数组开头。不会检查是否已添加监听器。多次调用事件名和监听器的相同组合将导致监听器被多次添加和调用。
返回对 EventEmitter 的引用,以便进行链式调用。
server.prependListener('connection', (stream) => {console.log('someone connected!');
});
emitter.prependOnceListener(eventName, listener) 前面添加一次性监听器
将名为 eventName 的事件的一次性监听函数添加到监听器数组的开头。下次事件名称被触发时,该监听器将被移除,然后再被调用。
server.prependOnceListener('connection', (stream) => {console.log('Ah, we have our first user!');
});
返回对 EventEmitter 的引用,以便进行链式调用。
emitter.removeAllListeners([eventName]) 移除指定名称的所有监听器
移除所有监听器或指定事件名称的监听器。
移除代码中其他地方添加的监听器是不好的做法,尤其是当 EventEmitter 实例是由其他组件或模块(如套接字或文件流)创建时。
返回对 EventEmitter 的引用,以便进行链式调用。
emitter.removeListener(eventName, listener) 移除指定名称的监听器
从名为 eventName 的事件的监听器数组中移除指定的监听器。
const callback = (stream) => {console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
removeListener() 最多会从监听器数组中移除一个监听器实例。如果任何一个监听器被多次添加到指定事件名的监听器数组中,则必须多次调用 removeListener() 才能移除每个实例。
事件发出后,在事件发出时附加到事件上的所有监听器都会被依次调用。这意味着,在事件发生后、最后一个监听器执行完毕前,任何 removeListener() 或 removeAllListeners() 调用都不会将它们从正在进行的 emit() 中移除。后续事件的行为与预期一致。
import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();const callbackA = () => {console.log('A');myEmitter.removeListener('event', callbackB);
};const callbackB = () => {console.log('B');
};myEmitter.on('event', callbackA);myEmitter.on('event', callbackB);// callbackA removes listener callbackB but it will still be called.
// Internal listener array at time of emit [callbackA, callbackB]
myEmitter.emit('event');
// Prints:
// A
// B// callbackB is now removed.
// Internal listener array [callbackA]
myEmitter.emit('event');
// Prints:
// A
由于监听器是使用内部数组管理的,因此调用此方法将改变在移除监听器后注册的任何监听器的位置索引。这不会影响监听器的调用顺序,但意味着需要重新创建 emitter.listeners() 方法返回的监听器数组副本。
当一个函数被多次添加为一个事件的处理程序时(如下面的示例),removeListener() 将移除最近添加的实例。在该示例中,once(‘ping’) 监听器被移除:
import { EventEmitter } from 'node:events';
const ee = new EventEmitter();function pong() {console.log('pong');
}ee.on('ping', pong);
ee.once('ping', pong);
ee.removeListener('ping', pong);ee.emit('ping');
ee.emit('ping');
返回对 EventEmitter 的引用,以便进行链式调用。
emitter.setMaxListeners(n) 设置最大监听器值
默认情况下,如果为某个特定事件添加的监听器超过 10 个,EventEmitters 将打印警告。这是一个有用的默认设置,有助于发现内存泄漏。通过 emitter.setMaxListeners() 方法,可以修改此特定 EventEmitter 实例的限制。该值可设置为无限(或 0),以表示监听器数量不受限制。
返回对 EventEmitter 的引用,以便进行链式调用。
const ee = new EventEmitter({ captureRejections: true });
for (let index = 0; index < 11; index++) {ee.on("something", async (value) => {console.log(index);});
}
ee.emit("something");
emitter.rawListeners(eventName) 获取指定侦听器副本
返回名为 eventName 的事件的侦听器数组副本,包括任何封装器(例如由 .once() 创建的封装器)。
import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.once('log', () => console.log('log once'));// Returns a new Array with a function `onceWrapper` which has a property
// `listener` which contains the original listener bound above
const listeners = emitter.rawListeners('log');
const logFnWrapper = listeners[0];// Logs "log once" to the console and does not unbind the `once` event
logFnWrapper.listener();// Logs "log once" to the console and removes the listener
logFnWrapper();emitter.on('log', () => console.log('log persistently'));
// Will return a new Array with a single function bound by `.on()` above
const newListeners = emitter.rawListeners('log');// Logs "log persistently" twice
newListeners[0]();
emitter.emit('log');
emitter[Symbol.for(‘nodejs.rejection’)](err, eventName[, …args]) 拒绝承诺函数
Symbol.for(‘nodejs.rejection’) 方法会在发出事件时发生允诺拒绝并在发出者上启用捕获拒绝的情况下被调用。可以使用 events.captureRejectionSymbol 代替 Symbol.for(‘nodejs.rejection’)。
import { EventEmitter, captureRejectionSymbol } from 'node:events';class MyClass extends EventEmitter {constructor() {super({ captureRejections: true });}[captureRejectionSymbol](err, event, ...args) {console.log('rejection happened for', event, 'with', err, ...args);this.destroy(err);}destroy(err) {// Tear the resource down here.}
}
events.defaultMaxListeners 默认最大监听器值
默认情况下,单个事件最多可注册 10 个监听者。
可以使用 emitter.setMaxListeners(n) 方法更改单个 EventEmitter 实例的这一限制。要更改所有 EventEmitter 实例的默认值,可使用 events.defaultMaxListeners 属性。如果该值不是正数,将产生 RangeError 错误。
在设置 events.defaultMaxListeners 时要小心,因为这一更改会影响所有 EventEmitter 实例,包括在更改之前创建的实例。不过,调用 emitter.setMaxListeners(n) 仍优先于 events.defaultMaxListeners。
这并非硬性限制。EventEmitter 实例允许添加更多监听器,但会向 stderr 输出跟踪警告,表明已检测到 “EventEmitter 内存可能泄漏”。对于任何单个 EventEmitter,可以使用 emitter.getMaxListeners() 和 emitter.setMaxListeners() 方法来暂时避免该警告:
import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {// do stuffemitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});
可以使用 --trace-warnings 命令行标志来显示此类警告的堆栈跟踪。
发出的警告可通过 process.on(‘warning’) 查看,并将具有额外的发射器、类型和计数属性,分别指事件发射器实例、事件名称和附加监听器的数量。其 name 属性设置为 “MaxListenersExceededWarning”。
events.errorMonitor 监听错误监听器
该符号用于安装仅用于监控 "错误 "事件的监听器。使用此符号安装的监听器会在调用常规 "错误 "监听器之前被调用。
使用此符号安装监听器不会改变 "错误 "事件发生后的行为。因此,如果没有安装常规的 "错误 "监听器,进程仍会崩溃。
events.getEventListeners(emitterOrTarget, eventName) 获取指定事件监听器数组副本
返回名为 eventName 的事件的侦听器数组副本。
对于事件发射器,这与在发射器上调用 .listeners 的行为完全相同。
对于事件目标,这是获取事件目标的事件侦听器的唯一方法。这对调试和诊断非常有用。
import { getEventListeners, EventEmitter } from 'node:events';{const ee = new EventEmitter();const listener = () => console.log('Events are fun');ee.on('foo', listener);console.log(getEventListeners(ee, 'foo')); // [ [Function: listener] ]
}
{const et = new EventTarget();const listener = () => console.log('Events are fun');et.addEventListener('foo', listener);console.log(getEventListeners(et, 'foo')); // [ [Function: listener] ]
}
events.getMaxListeners(emitterOrTarget) 获取当前监听器最大数量
返回当前设置的最大监听器数量。
对于 EventEmitters,这与在发射器上调用 .getMaxListeners 的行为完全相同。
对于事件目标,这是获取事件目标最大事件侦听器的唯一方法。如果单个 EventTarget 上的事件处理程序数量超过了所设置的最大值,EventTarget 将打印警告。
import { getMaxListeners, setMaxListeners, EventEmitter } from 'node:events';{const ee = new EventEmitter();console.log(getMaxListeners(ee)); // 10setMaxListeners(11, ee);console.log(getMaxListeners(ee)); // 11
}
{const et = new EventTarget();console.log(getMaxListeners(et)); // 10setMaxListeners(11, et);console.log(getMaxListeners(et)); // 11
}
events.once(emitter, name[, options]) 创建监听emitter错误的promise
创建一个 Promise,当 EventEmitter 发出给定事件时,该 Promise 将被执行;如果 EventEmitter 在等待期间发出 “error”,该 Promise 将被拒绝。该 Promise 将以数组的形式解析给定事件的所有参数。
该方法有意采用通用方法,与网络平台 EventTarget 接口配合使用,后者没有特殊的 "错误 "事件语义,也不监听 "错误 "事件。
import { once, EventEmitter } from 'node:events';
import process from 'node:process';const ee = new EventEmitter();process.nextTick(() => {ee.emit('myevent', 42);
});const [value] = await once(ee, 'myevent');
console.log(value);const err = new Error('kaboom');
process.nextTick(() => {ee.emit('error', err);
});try {await once(ee, 'myevent');
} catch (err) {console.error('error happened', err);
}
对 "error"事件的特殊处理仅在使用 events.once() 等待其他事件时才会使用。如果 events.once() 被用来等待 "错误 "事件本身,那么它就会被当作任何其他类型的事件来处理,而不会被特殊处理:
import { EventEmitter, once } from 'node:events';const ee = new EventEmitter();once(ee, 'error').then(([err]) => console.log('ok', err.message)).catch((err) => console.error('error', err.message));ee.emit('error', new Error('boom'));// Prints: ok boom
可以使用 <AbortSignal> 取消对事件的等待:
import { EventEmitter, once } from 'node:events';const ee = new EventEmitter();
const ac = new AbortController();async function foo(emitter, event, signal) {try {await once(emitter, event, { signal });console.log('event emitted!');} catch (error) {if (error.name === 'AbortError') {console.error('Waiting for the event was canceled!');} else {console.error('There was an error', error.message);}}
}foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!
Awaiting multiple events emitted on process.nextTick() 等待 process.nextTick() 发出的多个事件
在使用 events.once() 函数等待同一批 process.nextTick() 操作中发出的多个事件时,或在同步发出多个事件时,有一种边缘情况值得注意。
具体来说,由于 process.nextTick() 队列会在 Promise 微任务队列之前耗尽,而且 EventEmitter 会同步发出所有事件,因此 events.once() 有可能错过一个事件。
import { EventEmitter, once } from 'node:events';
import process from 'node:process';const myEE = new EventEmitter();async function foo() {await once(myEE, 'bar');console.log('bar');// This Promise will never resolve because the 'foo' event will// have already been emitted before the Promise is created.// 无法触发,因为在创建promise之前已经触发foo事件await once(myEE, 'foo');console.log('foo');
}process.nextTick(() => {myEE.emit('bar');myEE.emit('foo');
});foo().then(() => console.log('done'));
要捕捉这两个事件,应在等待其中任何一个事件之前创建每个 Promise,然后就可以使用 Promise.all()、Promise.race() 或 Promise.allSettled():
import { EventEmitter, once } from 'node:events';
import process from 'node:process';const myEE = new EventEmitter();async function foo() {await Promise.all([once(myEE, 'bar'), once(myEE, 'foo')]);console.log('foo', 'bar');
}process.nextTick(() => {myEE.emit('bar');myEE.emit('foo');
});foo().then(() => console.log('done'));
events.captureRejections 全局 captureRejections 选项
更改所有新 EventEmitter 对象的默认 captureRejections 选项。
events.captureRejectionSymbol
值:Symbol.for(‘nodejs.rejection’)
events.listenerCount(emitter, eventName)
类方法,用于返回在给定发射器上注册的给定事件名的监听者数量。
已弃用:使用 emitter.listenerCount() 代替。
events.on(emitter, eventName[, options]) 用于迭代由发射器发射的 eventName 事件
参数:
- emitter —— 发射器 <EventEmitter>
- eventName —— 正在监听的事件名称。
- options —— 配置对象
- signal —— 可用于取消等待事件。
- close —— 将结束迭代的事件名称。
- highWaterMark —— 默认值:Number.MAX_SAFE_INTEGER 高水印。每当缓冲事件的大小大于该值时,发射器就会暂停。仅支持执行了 pause() 和 resume() 方法的发射器。
-lowWaterMark —— 默认值:1 低水印。每当缓冲事件的大小低于该值时,发射器就会恢复。仅支持执行了 pause() 和 resume() 方法的发射器。
- 返回<AsyncIterator>,迭代由发射器发射的 eventName 事件
import { on, EventEmitter } from 'node:events';
import process from 'node:process';const ee = new EventEmitter();// Emit later on
process.nextTick(() => {ee.emit('foo', 'bar');ee.emit('foo', 42);
});for await (const event of on(ee, 'foo')) {// The execution of this inner block is synchronous and it// processes one event at a time (even with await). Do not use// if concurrent execution is required.console.log(event); // prints ['bar'] [42]
}
// Unreachable here
返回迭代 eventName 事件的 AsyncIterator。如果 EventEmitter 发出 “错误”,它将抛出。退出循环时,它会删除所有监听器。每次迭代返回的值是一个由已发出的事件参数组成的数组。
<AbortSignal> 可用于取消对事件的等待:
import { on, EventEmitter } from 'node:events';
import process from 'node:process';const ac = new AbortController();(async () => {const ee = new EventEmitter();// Emit later onprocess.nextTick(() => {ee.emit('foo', 'bar');ee.emit('foo', 42);});for await (const event of on(ee, 'foo', { signal: ac.signal })) {// The execution of this inner block is synchronous and it// processes one event at a time (even with await). Do not use// if concurrent execution is required.console.log(event); // prints ['bar'] [42]}// Unreachable here
})();process.nextTick(() => ac.abort());
events.setMaxListeners(n[, …eventTargets]) 设置最大监听器数量
设置最大指定监听器的数量,eventTargets没有则 n 将被设置为所有新创建的 <EventTarget> 和 <EventEmitter> 对象的默认最大值。
Class: events.EventEmitterAsyncResource extends EventEmitter
将 EventEmitter 与 <AsyncResource> 集成,用于需要手动异步跟踪的 EventEmitter。具体来说,由 events.EventEmitterAsyncResource 实例发出的所有事件都将在其异步上下文中运行。
import { EventEmitterAsyncResource, EventEmitter } from "node:events";
import { notStrictEqual, strictEqual } from "node:assert";
import { executionAsyncId, triggerAsyncId } from "node:async_hooks";// 异步跟踪工具会将其标识为“Q”。
const ee1 = new EventEmitterAsyncResource({ name: "Q" });// 'foo'监听器将在eventemitter异步上下文中运行。
ee1.on("foo", () => {strictEqual(executionAsyncId(), ee1.asyncId);console.log(executionAsyncId(), triggerAsyncId());strictEqual(triggerAsyncId(), ee1.triggerAsyncId);
});const ee2 = new EventEmitter();// 对于不跟踪异步上下文的普通 EventEmitter 上的 'foo' 监听器,其运行在与 emit() 相同的异步上下文中。
ee2.on("foo", () => {notStrictEqual(executionAsyncId(), ee2.asyncId);notStrictEqual(triggerAsyncId(), ee2.triggerAsyncId);console.log(executionAsyncId(), triggerAsyncId());
});Promise.resolve().then(() => {ee1.emit("foo");ee2.emit("foo");
});
EventEmitterAsyncResource 类的方法和选项与 EventEmitter 和 AsyncResource 本身相同。
new events.EventEmitterAsyncResource([options]) 创建EventEmitterAsyncResource实例
参数:
- 选项
- captureRejections <布尔> 启用自动捕获承诺拒绝。默认值:false。
- name <string> 异步事件的类型。默认:new.target.name。
- triggerAsyncId <number> 创建此异步事件的执行上下文的 ID。默认: executionAsyncId()。
- requireManualDestroy <boolean>如果设置为 “true”,则在对象被垃圾回收时禁用 emitDestroy。通常不需要设置此项(即使手动调用了 emitDestroy),除非检索到资源的 asyncId 并调用了敏感 API 的 emitDestroy。当设置为 false 时,只有在至少有一个活动的 destroy 钩子时,才会在垃圾回收时调用 emitDestroy。默认值:false。
eventemitterasyncresource.asyncId 分配给资源的唯一 asyncId。
分配给资源的唯一 asyncId。
eventemitterasyncresource.asyncResource 底层 <AsyncResource>。
返回的 AsyncResource 对象有一个额外的 eventEmitter 属性,该属性提供了对该 EventEmitterAsyncResource 的引用。
eventemitterasyncresource.emitDestroy() 销毁事件
调用所有销毁钩子。只能调用一次。如果调用超过一次,将产生错误。必须手动调用。如果资源被留待 GC 收集,则销毁钩子将永远不会被调用。
eventemitterasyncresource.triggerAsyncId
与传递给 AsyncResource 构造函数的 triggerAsyncId 相同。
EventTarget and Event API
EventTarget 和 Event 对象是一些 Node.js 核心 API 所公开的 EventTarget Web API 的特定 Node.js 实现。
const target = new EventTarget();target.addEventListener('foo', (event) => {console.log('foo event happened!');
});
Node.js EventTarget vs. DOM EventTarget
Node.js EventTarget 和 EventTarget Web API 之间有两个主要区别:
- DOM EventTarget 实例可能是分层的,而 Node.js 中没有分层和事件传播的概念。也就是说,派发到 EventTarget 的事件不会通过嵌套目标对象的层次结构传播,这些嵌套目标对象可能各自有自己的事件处理程序集。
- 在 Node.js EventTarget 中,如果事件监听器是一个异步函数或返回一个 Promise,并且返回的 Promise 发生拒绝,则会自动捕获拒绝,并以与同步抛出的监听器相同的方式进行处理(详见 EventTarget 错误处理)。
NodeEventTarget 比较 EventEmitter
NodeEventTarget 对象实现了 EventEmitter API 的一个修改子集,可在某些情况下近似模拟 EventEmitter。NodeEventTarget 不是 EventEmitter 的实例,在大多数情况下不能代替 EventEmitter 使用。
- 与 EventEmitter 不同,每个事件类型的监听器最多只能注册一次。多次注册监听器的尝试将被忽略。
- NodeEventTarget 并不模拟完整的 EventEmitter API。具体来说,它没有模拟 prependListener()、prependOnceListener()、rawListeners() 和 errorMonitor API。newListener "和 "removeListener "事件也不会发出。
- 对于类型为 "error "的事件,NodeEventTarget 没有实现任何特殊的默认行为。
- NodeEventTarget 支持将 EventListener 对象和函数作为所有事件类型的处理程序。
Event listener 事件监听器
为事件类型注册的事件监听器可以是 JavaScript 函数,也可以是带有 handleEvent 属性(其值为函数)的对象。
无论哪种情况,处理程序函数都会调用传递给 eventTarget.dispatchEvent() 函数的事件参数。
异步函数可用作事件监听器。如果异步处理函数拒绝接受,则会捕获拒绝并按照 EventTarget 错误处理中的描述进行处理。
一个处理函数抛出的错误不会阻止其他处理函数被调用。
处理函数的返回值将被忽略。
处理程序总是按照添加的顺序调用。
处理函数可以更改事件对象。
function handler1(event) {console.log(event.type); // Prints 'foo'event.a = 1;
}async function handler2(event) {console.log(event.type); // Prints 'foo'console.log(event.a); // Prints 1
}const handler3 = {handleEvent(event) {console.log(event.type); // Prints 'foo'},
};const handler4 = {async handleEvent(event) {console.log(event.type); // Prints 'foo'},
};const target = new EventTarget();target.addEventListener('foo', handler1);
target.addEventListener('foo', handler2);
target.addEventListener('foo', handler3);
target.addEventListener('foo', handler4, { once: true });
EventTarget error handling EventTarget 错误处理
当注册的事件监听器抛出(或返回拒绝的 Promise)时,默认情况下,该错误会在 process.nextTick() 中被视为未捕获异常。这意味着事件目标中的未捕获异常将默认终止 Node.js 进程。
在事件监听器中抛出不会阻止其他已注册处理程序被调用。
EventTarget 并没有像 EventEmitter 一样,为 "错误 "类型事件实现任何特殊的默认处理方式。
目前,错误会先转发到 process.on(‘error’) 事件,然后再转发到 process.on(‘uncaughtException’)。这一行为已被弃用,并将在未来的版本中进行修改,以使 EventTarget 与其他 Node.js API 保持一致。任何依赖 process.on(‘error’) 事件的代码都应与新行为保持一致。
Class: Event Event类
事件对象是对事件 Web API 的改编。实例由 Node.js 在内部创建。
event.bubbles
Node.js 中不使用此功能,提供此功能纯粹是为了完整。
event.cancelBubble
如果设置为 true,则是 event.stopPropagation() 的别名。Node.js 中不使用此函数,提供此函数纯粹是为了完整。
event.cancelable
如果创建事件时使用了可取消选项,则为 True。
event.composed
Node.js 中不使用此功能,提供此功能纯粹是为了完整。
event.composedPath()
返回一个数组,其中唯一的条目是当前事件目标(EventTarget),如果事件未被派发,则返回空数组。Node.js 中不使用此参数,提供此参数纯粹是为了完整。
event.currentTarget
event.target 的别名。
event.defaultPrevented
如果 cancelable 为 true 且调用了 event.preventDefault(),则为 true。
event.eventPhase
Node.js 中不使用此功能,提供此功能纯粹是为了完整。
event.initEvent(type[, bubbles[, cancelable]])
与事件构造函数冗余,无法设置组成。Node.js 中未使用此功能,提供此功能纯粹是为了完整。
event.isTrusted
<AbortSignal> "中止 "事件在 isTrusted 设置为 true 时发出。在所有其他情况下,该值为 false。
event.preventDefault()
如果可取消为 true,则将 defaultPrevented 属性设置为 true。
event.returnValue
event.returnValue 的值总是与 event.defaultPrevented 相反。Node.js 中不使用此值,提供此值纯粹是为了完整。
event.srcElement
event.target 的别名。
遗留问题:使用 event.target 代替。
event.stopImmediatePropagation()
Node.js 中不使用此功能,提供此功能纯粹是为了完整。
event.stopPropagation()
Node.js 中不使用此功能,提供此功能纯粹是为了完整。
event.target
调度事件的 EventTarget。
event.timeStamp
事件对象创建时的毫秒时间戳。
event.type
事件类型标识符。
Class: EventTarget
eventTarget.addEventListener(type, listener[, options])
参数:
- type —— 字符串
- listener<函数> | <事件监听器>
- options<对象
- once <布尔值> 为 true 时,监听器会在首次调用时自动移除。默认值:false。
- passive <boolean> 为真时,提示监听器不会调用事件对象的 preventDefault() 方法。默认值:false。
- capture <布尔> Node.js 不直接使用。为使 API 更完整而添加。默认值:false。
- signal <AbortSignal> 当调用给定 AbortSignal 对象的 abort() 方法时,监听器将被移除。
为类型事件添加新的处理程序。每个类型和每个捕获选项值只能添加一次监听器。
如果 once 选项为 “true”,监听器将在下一次类型事件派发后移除。
除了根据 EventTarget 规范跟踪已注册的事件监听器外,Node.js 不会以任何功能方式使用捕获选项。具体来说,捕获选项在注册监听器时被用作密钥的一部分。任何一个监听器都可以在 capture = false 和 capture = true 时分别添加一次。
function handler(event) {}const target = new EventTarget();
target.addEventListener('foo', handler, { capture: true }); // first
target.addEventListener('foo', handler, { capture: false }); // second// Removes the second instance of handler
target.removeEventListener('foo', handler);// Removes the first instance of handler
target.removeEventListener('foo', handler, { capture: true });
eventTarget.dispatchEvent(event)
将事件分派给事件类型的处理程序列表。
已注册的事件监听器会按照注册顺序被同步调用。
eventTarget.removeEventListener(type, listener[, options])
从事件类型的处理程序列表中删除监听器。
Class: CustomEvent
CustomEvent 对象是对 CustomEvent Web API 的改编。实例由 Node.js 在内部创建。
event.detail
返回初始化时传递的自定义数据。
Class: NodeEventTarget
NodeEventTarget 是 EventTarget 的 Node.js 特定扩展,它模拟了 EventEmitter API 的一个子集。
nodeEventTarget.addListener(type, listener) 添加
事件目标(EventTarget)类的 Node.js 特定扩展,可模拟等效的 EventEmitter API。addListener() 和 addEventListener() 之间的唯一区别是,addListener() 将返回对 EventTarget 的引用。
nodeEventTarget.emit(type, arg) 触发
事件目标(EventTarget)类的 Node.js 特定扩展,用于将 arg 发送到类型的处理程序列表。
nodeEventTarget.eventNames()
事件目标(EventTarget)类的 Node.js 特定扩展,用于返回已注册事件监听器的事件类型名称数组。
nodeEventTarget.listenerCount(type)
EventTarget 类的 Node.js 特定扩展,用于返回为该类型注册的事件监听器数量。
nodeEventTarget.setMaxListeners(n)
事件目标(EventTarget)类的 Node.js 特定扩展,可将最大事件侦听器数量设置为 n。
nodeEventTarget.getMaxListeners()
事件目标(EventTarget)类的 Node.js 特定扩展,用于返回最大事件侦听器的数量。
nodeEventTarget.off(type, listener[, options])
eventTarget.removeEventListener() 的 Node.js 专用别名。
nodeEventTarget.on(type, listener)
eventTarget.addEventListener() 的 Node.js 专用别名。
nodeEventTarget.once(type, listener)
事件目标(EventTarget)类的 Node.js 特定扩展,可为给定的事件类型添加一次监听器。这相当于在调用 on 时将 once 选项设为 true。
nodeEventTarget.removeAllListeners([type])
事件目标(EventTarget)类的 Node.js 特定扩展。如果指定了类型,则移除该类型的所有已注册侦听器,否则移除所有已注册侦听器。
nodeEventTarget.removeListener(type, listener[, options])
Node.js 对 EventTarget 类的特定扩展,用于移除给定类型的监听器。removeListener() 与 removeEventListener() 的唯一区别是,removeListener() 将返回对 EventTarget 的引用。
结语
结束了。