events.once(emitter, name[, options])#

History
Version版本Changes变更
v15.0.0

The signal option is supported now.现在支持signal选项。

v11.13.0, v10.16.0

Added in: v11.13.0, v10.16.0

Creates a Promise that is fulfilled when the EventEmitter emits the given event or that is rejected if the EventEmitter emits 'error' while waiting. 创建一个Promise,该Promise在EventEmitter发出给定事件时实现,或者如果EventEmitter在等待时发出'error'则被拒绝。The Promise will resolve with an array of all the arguments emitted to the given event.Promise将使用向给定事件发出的所有参数的数组进行解析。

This method is intentionally generic and works with the web platform EventTarget interface, which has no special 'error' event semantics and does not listen to the 'error' event.此方法是故意通用的,并与web平台EventTarget接口一起使用,后者没有特殊的'error'事件语义,也不侦听'error'消息。

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.log('error happened', err);
}
const { once, EventEmitter } = require('node: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();

The special handling of the 'error' event is only used when events.once() is used to wait for another event. 只有当events.once()用于等待另一个事件时,才会使用'error'事件的特殊处理。If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:如果eventsonce()用于等待'error'事件本身,那么它将被视为任何其他类型的事件,而无需特殊处理:

import { EventEmitter, once } from 'node:events';

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom
const { EventEmitter, once } = require('node:events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An <AbortSignal> can be used to cancel waiting for the event:<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!
const { EventEmitter, once } = require('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()上发出的多个事件#

There is an edge case worth noting when using the events.once() function to await multiple events emitted on in the same batch of process.nextTick() operations, or whenever multiple events are emitted synchronously. 当使用events.once()函数等待在同一批process.nextTick()操作中发出的多个事件时,或者当多个事件同时发出时,有一个边缘情况值得注意。Specifically, because the process.nextTick() queue is drained before the Promise microtask queue, and because EventEmitter emits all events synchronously, it is possible for events.once() to miss an event.具体来说,由于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.
await once(myEE, 'foo');
console.log('foo');
}

process.nextTick(() => {
myEE.emit('bar');
myEE.emit('foo');
});

foo().then(() => console.log('done'));
const { EventEmitter, once } = require('node:events');

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.
await once(myEE, 'foo');
console.log('foo');
}

process.nextTick(() => {
myEE.emit('bar');
myEE.emit('foo');
});

foo().then(() => console.log('done'));

To catch both events, create each of the Promises before awaiting either of them, then it becomes possible to use Promise.all(), Promise.race(), or Promise.allSettled():要捕获这两个事件,请在等待其中一个之前创建每个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'));
const { EventEmitter, once } = require('node:events');

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