diff --git a/doc/api/events.md b/doc/api/events.md index 0dd9e4be5dbc6d..c502cbe83f4427 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -419,14 +419,14 @@ myEE.emit('foo'); // a ``` -### emitter.once(eventName, listener) +### emitter.once(eventName[, listener]) * `eventName` {string|symbol} The name of the event. * `listener` {Function} The callback function -* Returns: {EventEmitter} +* Returns: {EventEmitter|Promise} Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. @@ -439,6 +439,16 @@ server.once('connection', (stream) => { Returns a reference to the `EventEmitter`, so that calls can be chained. +If a listener function isn't passed - returns a `Promise` for the event happening: + +```js +(async () => { + console.log('connecting'); + const stream = await server.once('connection'); + console.log('connected'); +})(); +``` + By default, event listeners are invoked in the order they are added. The `emitter.prependOnceListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. diff --git a/lib/events.js b/lib/events.js index ff1648d6aa13e7..89bb05f5bdcaa8 100644 --- a/lib/events.js +++ b/lib/events.js @@ -284,8 +284,8 @@ function _onceWrap(target, type, listener) { EventEmitter.prototype.once = function once(type, listener) { if (typeof listener !== 'function') { - const errors = lazyErrors(); - throw new errors.ERR_INVALID_ARG_TYPE('listener', 'Function', listener); + return new Promise((resolve) => + this.on(type, _onceWrap(this, type, resolve))); } this.on(type, _onceWrap(this, type, listener)); return this; diff --git a/test/parallel/test-event-emitter-once.js b/test/parallel/test-event-emitter-once.js index 1bdf0cbf126aaf..918eaabf51b28c 100644 --- a/test/parallel/test-event-emitter-once.js +++ b/test/parallel/test-event-emitter-once.js @@ -26,6 +26,8 @@ const EventEmitter = require('events'); const e = new EventEmitter(); +common.crashOnUnhandledRejection(); + e.once('hello', common.mustCall()); e.emit('hello', 'a', 'b'); @@ -79,3 +81,14 @@ common.expectsError(() => { EventEmitter.prototype.emit.apply(ee, args); } } + +{ + // verify the promise returning version of `once` + async function doTest() { + const ee = new EventEmitter(); + setTimeout(() => ee.emit('hello'), 0); + await ee.once('hello'); + } + + doTest().then(common.mustCall()); +}