新彩天欢迎您!
幻海优品

事件(Events) | Node.js

Node.js v8.x 中文文档


events (事件)#

稳定性: 2 - 稳定的

大多数 Node.js 核心 API 都采用惯用的异步事件驱动架构,其中某些类型的对象(触发器)会周期性地触发命名事件来调用函数对象(监听器)。

例如,net.Server 对象会在每次有新连接时触发事件;fs.ReadStream 会在文件被打开时触发事件;流对象 会在数据可读时触发事件。

所有能触发事件的对象都是 EventEmitter 类的实例。这些对象开放了一个 eventEmitter.on() 函数,允许将一个或多个函数绑定到会被对象触发的命名事件上。事件名称通常是驼峰式的字符串,但也可以使用任何有效的 JavaScript 属性名。

EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都被同步地调用。监听器的返回值会被丢弃。

例子,一个绑定了一个监听器的 EventEmitter 实例。eventEmitter.on() 方法用于注册监听器,eventEmitter.emit() 方法用于触发事件。

const EventEmitter = require('events');class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();myEmitter.on('event', () => {  console.log('触发了一个事件!');});myEmitter.emit('event');

给监听器传入参数与 this#

eventEmitter.emit() 方法允许将任意参数传给监听器函数。当一个普通的监听器函数被 EventEmitter 调用时,标准的 this 关键词会被设置指向监听器所附加的 EventEmitter

const myEmitter = new MyEmitter();myEmitter.on('event', function(a, b) {  console.log(a, b, this);  // 打印:  //   a b MyEmitter {  //     domain: null,  //     _events: { event: [Function] },  //     _eventsCount: 1,  //     _maxListeners: undefined }});myEmitter.emit('event', 'a', 'b');

也可以使用 ES6 的箭头函数作为监听器。但是这样 this 关键词就不再指向 EventEmitter 实例:

const myEmitter = new MyEmitter();myEmitter.on('event', (a, b) => {  console.log(a, b, this);  // 打印: a b {}});myEmitter.emit('event', 'a', 'b');

异步与同步#

EventEmitter 会按照监听器注册的顺序同步地调用所有监听器。所以需要确保事件的正确排序且避免竞争条件或逻辑错误。监听器函数可以使用 setImmediate()process.nextTick() 方法切换到异步操作模式:

const myEmitter = new MyEmitter();myEmitter.on('event', (a, b) => {  setImmediate(() => {    console.log('这个是异步发生的');  });});myEmitter.emit('event', 'a', 'b');

只处理事件一次#

当使用 eventEmitter.on() 方法注册监听器时,监听器会在每次触发命名事件时被调用。

const myEmitter = new MyEmitter();let m = 0;myEmitter.on('event', () => {  console.log(++m);});myEmitter.emit('event');// 打印: 1myEmitter.emit('event');// 打印: 2

使用 eventEmitter.once() 方法时可以注册一个对于特定事件最多被调用一次的监听器。当事件被触发时,监听器会被注销,然后再调用。

const myEmitter = new MyEmitter();let m = 0;myEmitter.once('event', () => {  console.log(++m);});myEmitter.emit('event');// 打印: 1myEmitter.emit('event');// 忽略

错误事件#

EventEmitter 实例中发生错误时,会触发一个 'error' 事件。这在 Node.js 中是特殊情况。

如果 EventEmitter 没有为 'error' 事件注册至少一个监听器,则当 'error' 事件触发时,会抛出错误、打印堆栈跟踪、且退出 Node.js 进程。

const myEmitter = new MyEmitter();myEmitter.emit('error', new Error('whoops!'));// 抛出错误,并使 Node.js 崩溃

为了防止 Node.js 进程崩溃,可以在使用 domain 模块。(注意,domain 模块已被废弃。)

作为最佳实践,应该始终为 'error' 事件注册监听器。

const myEmitter = new MyEmitter();myEmitter.on('error', (err) => {  console.error('有错误');});myEmitter.emit('error', new Error('whoops!'));// 打印: 有错误

EventEmitter 类#

EventEmitter 类由 events 模块定义和开放的:

const EventEmitter = require('events');

当新的监听器被添加时,所有的 EventEmitter 会触发 'newListener' 事件;当移除已存在的监听器时,则触发 'removeListener'

'newListener' 事件#

  • eventName <any> 要监听的事件的名称
  • listener <Function> 事件的句柄函数

EventEmitter 实例会在一个监听器被添加到其内部监听器数组之前触发自身的 'newListener' 事件。

注册了 'newListener' 事件的监听器会传入事件名与被添加的监听器的引用。

事实上,在添加监听器之前触发事件有一个微妙但重要的副作用:在'newListener' 回调函数中, 一个监听器的名字如果和已有监听器名称相同, 则在被插入到EventEmitter实例的内部监听器数组时, 该监听器会被添加到其它同名监听器的前面。

const myEmitter = new MyEmitter();// 只处理一次,所以不会无限循环myEmitter.once('newListener', (event, listener) => {  if (event === 'event') {    // 在开头插入一个新的监听器    myEmitter.on('event', () => {      console.log('B');    });  }});myEmitter.on('event', () => {  console.log('A');});myEmitter.emit('event');// 打印://   B//   A

'removeListener' 事件#

  • eventName <any> 事件名
  • listener <Function> 事件句柄函数

'removeListener' 事件在 listener 被移除后触发。

EventEmitter.listenerCount(emitter, eventName)#

稳定性: 0 - 废弃的: 使用 emitter.listenerCount() 代替。

A class method that returns the number of listeners for the given eventNameregistered on the given emitter.

const myEmitter = new MyEmitter();myEmitter.on('event', () => {});myEmitter.on('event', () => {});console.log(EventEmitter.listenerCount(myEmitter, 'event'));// Prints: 2

EventEmitter.defaultMaxListeners#

每个事件默认可以注册最多 10 个监听器。单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。如果这个值不是正数, 那将抛出 TypeError错误.

设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners

注意,这不是一个硬性限制。EventEmitter 实例允许添加更多的监听器,但会向 stderr 输出跟踪警告,表明检测到一个可能的 EventEmitter 内存泄漏。对于任何单个 EventEmitter 实例,emitter.getMaxListeners()emitter.setMaxListeners() 方法可用于暂时地消除此警告:

emitter.setMaxListeners(emitter.getMaxListeners() + 1);emitter.once('event', () => {  // 做些操作  emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));});

--trace-warnings 命令行标志可用于显示此类警告的堆栈跟踪。

触发的警告可以使用 process.on('warning') 检查,还有额外的 emittertypecount 属性,分别代表事件触发器实例的引用、事件的名称、和附加的监听器的数量。其 name 属性设置为 MaxListenersExceededWarning

emitter.addListener(eventName, listener)#

emitter.on(eventName, listener) 的别名。

emitter.emit(eventName[, ...args])#

  • eventName <any>
  • ...args <any>

按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。

如果事件有监听器,则返回 true ,否则返回 false

emitter.eventNames()#

返回一个列出触发器已注册监听器的事件的数组。数组中的值为字符串或符号。

const EventEmitter = require('events');const myEE = new EventEmitter();myEE.on('foo', () => {});myEE.on('bar', () => {});const sym = Symbol('symbol');myEE.on(sym, () => {});console.log(myEE.eventNames());// 打印: [ 'foo', 'bar', Symbol(symbol) ]

emitter.getMaxListeners()#

返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners

emitter.listenerCount(eventName)#

  • eventName <any> 正在被监听的事件名

返回正在监听名为 eventName 的事件的监听器的数量。

emitter.listeners(eventName)#

  • eventName <any>

返回名为 eventName 的事件的监听器数组的副本。

server.on('connection', (stream) => {  console.log('someone connected!');});console.log(util.inspect(server.listeners('connection')));// 打印: [ [Function] ]

emitter.on(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。不会检查 listener 是否已被添加。多次调用并传入相同的 eventNamelistener 会导致 listener 被添加与调用多次。

server.on('connection', (stream) => {  console.log('有连接!');});

返回一个 EventEmitter 引用,可以链式调用。

默认情况下,事件监听器会按照添加的顺序依次调用。emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。

const myEE = new EventEmitter();myEE.on('foo', () => console.log('a'));myEE.prependListener('foo', () => console.log('b'));myEE.emit('foo');// 打印://   b//   a

emitter.once(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加一个单次 listener 函数到名为 eventName 的事件。下次触发 eventName 事件时,监听器会被移除,然后调用。

server.once('connection', (stream) => {  console.log('首次调用!');});

返回一个 EventEmitter 引用,可以链式调用。

默认情况下,事件监听器会按照添加的顺序依次调用。emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。

const myEE = new EventEmitter();myEE.once('foo', () => console.log('a'));myEE.prependOnceListener('foo', () => console.log('b'));myEE.emit('foo');// 打印://   b//   a

emitter.prependListener(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加 listener 函数到名为 eventName 的事件的监听器数组的开头。不会检查 listener 是否已被添加。多次调用并传入相同的 eventNamelistener 会导致 listener 被添加与调用多次。

server.prependListener('connection', (stream) => {  console.log('有连接!');});

返回一个 EventEmitter 引用,可以链式调用。

emitter.prependOnceListener(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。下次触发 eventName 事件时,监听器会被移除,然后调用。

server.prependOnceListener('connection', (stream) => {  console.log('首次调用!');});

返回一个 EventEmitter 引用,可以链式调用。

emitter.removeAllListeners([eventName])#

  • eventName <any>

移除全部或指定 eventName 的监听器。

注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块(如 socket 或文件流)创建的。

返回一个 EventEmitter 引用,可以链式调用。

emitter.removeListener(eventName, listener)#

从名为 eventName 的事件的监听器数组中移除指定的 listener

const callback = (stream) => {  console.log('有连接!');};server.on('connection', callback);// ...server.removeListener('connection', callback);

removeListener 最多只会从监听器数组里移除一个监听器实例。如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。

注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener()removeAllListeners() 调用都不会从 emit() 中移除它们。随后的事件会像预期的那样发生。

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 移除了监听器 callbackB,但它依然会被调用。// 触发是内部的监听器数组为 [callbackA, callbackB]myEmitter.emit('event');// 打印://   A//   B// callbackB 被移除了。// 内部监听器数组为 [callbackA]myEmitter.emit('event');// 打印://   A

因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。

返回一个 EventEmitter 引用,可以链式调用。

emitter.setMaxListeners(n)#

默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。此限制有助于寻找内存泄露。但是,并不是所有的事件都要被限为 10 个。emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。值设为 Infinity(或 0)表明不限制监听器的数量。

返回一个 EventEmitter 引用,可以链式调用。

免责声明:以上内容(如有图片或视频亦包括在内)有转载其他网站资源,如有侵权请联系删除