Node.js v12.6.0 文档


events(事件触发器)#

中英对照提交修改

稳定性: 2 - 稳定

大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。

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

所有能触发事件的对象都是 EventEmitter 类的实例。 这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上。 事件的命名通常是驼峰式的字符串。

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() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, this 关键词会被指向监听器所绑定的 EventEmitter 实例。

const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
  console.log(a, b, this, this === myEmitter);
  // 打印:
  //   a b MyEmitter {
  //     domain: null,
  //     _events: { event: [Function] },
  //     _eventsCount: 1,
  //     _maxListeners: undefined } true
});
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');

异步 VS 同步#

中英对照提交修改

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');
// 打印: 1
myEmitter.emit('event');
// 打印: 2

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

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

错误事件#

中英对照提交修改

EventEmitter 实例出错时,应该触发 'error' 事件。

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

const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('错误信息'));
// 抛出错误

为了防止崩溃 Node.js 进程,可以使用 domain 模块。 (但请注意,不推荐使用 domain 模块。)

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

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

EventEmitter 类#

中英对照提交修改

EventEmitter 类由 events 模块定义:

const EventEmitter = require('events');

当新增监听器时,会触发 'newListener' 事件;当移除已存在的监听器时,则触发 'removeListener' 事件。

'newListener' 事件#

中英对照提交修改

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

'newListener' 事件注册的监听器将传递事件名称和对要添加的监听器的引用。

在添加监听器之前触发事件的事实具有微妙但重要的副作用:在 'newListener' 回调中注册到相同 name 的任何其他监听器将插入到正在添加的监听器之前。

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' 事件#

中英对照提交修改

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

EventEmitter.listenerCount(emitter, eventName)#

暂无中英对照

稳定性: 0 - 废弃: 改为使用 emitter.listenerCount()

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

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

EventEmitter.defaultMaxListeners#

查看v10.x中文文档

By default, a maximum of 10 listeners can be registered for any single event. This limit can be changed for individual EventEmitter instances using the emitter.setMaxListeners(n) method. To change the default for all EventEmitter instances, the EventEmitter.defaultMaxListeners property can be used. If this value is not a positive number, a TypeError will be thrown.

Take caution when setting the EventEmitter.defaultMaxListeners because the change affects all EventEmitter instances, including those created before the change is made. However, calling emitter.setMaxListeners(n) still has precedence over EventEmitter.defaultMaxListeners.

This is not a hard limit. The EventEmitter instance will allow more listeners to be added but will output a trace warning to stderr indicating that a "possible EventEmitter memory leak" has been detected. For any single EventEmitter, the emitter.getMaxListeners() and emitter.setMaxListeners() methods can be used to temporarily avoid this warning:

emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
  // do stuff
  emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});

The --trace-warnings command line flag can be used to display the stack trace for such warnings.

The emitted warning can be inspected with process.on('warning') and will have the additional emitter, type and count properties, referring to the event emitter instance, the event’s name and the number of attached listeners, respectively. Its name property is set to 'MaxListenersExceededWarning'.

emitter.addListener(eventName, listener)#

中英对照提交修改

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

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

中英对照提交修改

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

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

emitter.eventNames()#

中英对照提交修改

返回已注册监听器的事件名数组。 数组中的值为字符串或 Symbol

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 的事件的监听器的数量。

emitter.listeners(eventName)#

中英对照提交修改

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

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

emitter.off(eventName, listener)#

中英对照提交修改

emitter.removeListener() 的别名。

emitter.on(eventName, listener)#

中英对照提交修改

添加 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)#

中英对照提交修改

添加单次监听器 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)#

中英对照提交修改

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

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

返回对 EventEmitter 的引用,以便可以链式调用。

emitter.prependOnceListener(eventName, listener)#

中英对照提交修改

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

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

返回对 EventEmitter 的引用,以便可以链式调用。

emitter.removeAllListeners([eventName])#

查看v10.x中文文档

Removes all listeners, or those of the specified eventName.

It is bad practice to remove listeners added elsewhere in the code, particularly when the EventEmitter instance was created by some other component or module (e.g. sockets or file streams).

Returns a reference to the EventEmitter, so that calls can be chained.

emitter.removeListener(eventName, listener)#

查看v10.x中文文档

Removes the specified listener from the listener array for the event named eventName.

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

removeListener() will remove, at most, one instance of a listener from the listener array. If any single listener has been added multiple times to the listener array for the specified eventName, then removeListener() must be called multiple times to remove each instance.

Once an event has been emitted, all listeners attached to it at the time of emitting will be called in order. This implies that any removeListener() or removeAllListeners() calls after emitting and before the last listener finishes execution will not remove them from emit() in progress. Subsequent events will behave as expected.

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

Because listeners are managed using an internal array, calling this will change the position indices of any listener registered after the listener being removed. This will not impact the order in which listeners are called, but it means that any copies of the listener array as returned by the emitter.listeners() method will need to be recreated.

When a single function has been added as a handler multiple times for a single event (as in the example below), removeListener() will remove the most recently added instance. In the example the once('ping') listener is removed:

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');

Returns a reference to the EventEmitter, so that calls can be chained.

emitter.setMaxListeners(n)#

中英对照提交修改

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

返回对 EventEmitter 的引用,以便可以链式调用。

emitter.rawListeners(eventName)#

查看v10.x中文文档

Returns a copy of the array of listeners for the event named eventName, including any wrappers (such as those created by .once()).

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');

events.once(emitter, name)#

暂无中英对照提交修改

Creates a Promise that is resolved when the EventEmitter emits the given event or that is rejected when the EventEmitter emits 'error'. The Promise will resolve with an array of all the arguments emitted to the given event.

const { once, EventEmitter } = require('events');

async function run() {
  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.log('error happened', err);
  }
}

run();