Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 0 additions & 135 deletions doc/api/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -970,27 +970,6 @@ There are two key differences between the Node.js `EventTarget` and the
will be automatically captured and handled the same way as a listener that
throws synchronously (see [`EventTarget` Error Handling][] for details).

### `NodeEventTarget` vs. `EventEmitter`

The `NodeEventTarget` object implements a modified subset of the
`EventEmitter` API that allows it to closely *emulate* an `EventEmitter` in
certain situations. It is important to understand, however, that an
`NodeEventTarget` is *not* an instance of `EventEmitter` and cannot be used in
place of an `EventEmitter` in most cases.

1. Unlike `EventEmitter`, any given `listener` can be registered at most once
per event `type`. Attempts to register a `listener` multiple times will be
ignored.
2. The `NodeEventTarget` does not emulate the full `EventEmitter` API.
Specifically the `prependListener()`, `prependOnceListener()`,
`rawListeners()`, `setMaxListeners()`, `getMaxListeners()`, and
`errorMonitor` APIs are not emulated. The `'newListener'` and
`'removeListener'` events will also not be emitted.
3. The `NodeEventTarget` does not implement any special default behavior
for events with type `'error'`.
3. The `NodeEventTarget` supports `EventListener` objects as well as
functions as handlers for all event types.

### Event Listener

Event listeners registered for an event `type` may either be JavaScript
Expand Down Expand Up @@ -1277,120 +1256,6 @@ added: REPLACEME

Removes the `listener` from the list of handlers for event `type`.

### Class: `NodeEventTarget extends EventTarget`
<!-- YAML
added: REPLACEME
-->

The `NodeEventTarget` is a Node.js-specific extension to `EventTarget`
that emulates a subset of the `EventEmitter` API.

#### `nodeEventTarget.addListener(type, listener[, options])`
<!-- YAML
added: REPLACEME
-->

* `type` {string}
* `listener` {Function|EventListener}
* `options` {Object}
* `once` {boolean}

* Returns: {EventTarget} this

Node.js-specific extension to the `EventTarget` class that emulates the
equivalent `EventEmitter` API. The only difference between `addListener()` and
`addEventListener()` is that `addListener()` will return a reference to the
`EventTarget`.

#### `nodeEventTarget.eventNames()`
<!-- YAML
added: REPLACEME
-->

* Returns: {string[]}

Node.js-specific extension to the `EventTarget` class that returns an array
of event `type` names for which event listeners are currently registered.

#### `nodeEventTarget.listenerCount(type)`
<!-- YAML
added: REPLACEME
-->

* `type` {string}

* Returns: {number}

Node.js-specific extension to the `EventTarget` class that returns the number
of event listeners registered for the `type`.

#### `nodeEventTarget.off(type, listener)`
<!-- YAML
added: REPLACEME
-->

* `type` {string}
* `listener` {Function|EventListener}

* Returns: {EventTarget} this

Node.js-speciic alias for `eventTarget.removeListener()`.

#### `nodeEventTarget.on(type, listener[, options])`
<!-- YAML
added: REPLACEME
-->

* `type` {string}
* `listener` {Function|EventListener}
* `options` {Object}
* `once` {boolean}

* Returns: {EventTarget} this

Node.js-specific alias for `eventTarget.addListener()`.

#### `nodeEventTarget.once(type, listener[, options])`
<!-- YAML
added: REPLACEME
-->

* `type` {string}
* `listener` {Function|EventListener}
* `options` {Object}

* Returns: {EventTarget} this

Node.js-specific extension to the `EventTarget` class that adds a `once`
listener for the given event `type`. This is equivalent to calling `on`
with the `once` option set to `true`.

#### `nodeEventTarget.removeAllListeners([type])`
<!-- YAML
added: REPLACEME
-->

* `type` {string}

Node.js-specific extension to the `EventTarget` class. If `type` is specified,
removes all registered listeners for `type`, otherwise removes all registered
listeners.

#### `nodeEventTarget.removeListener(type, listener)`
<!-- YAML
added: REPLACEME
-->

* `type` {string}
* `listener` {Function|EventListener}

* Returns: {EventTarget} this

Node.js-specific extension to the `EventTarget` class that removes the
`listener` for the given `type`. The only difference between `removeListener()`
and `removeEventListener()` is that `removeListener()` will return a reference
to the `EventTarget`.

[WHATWG-EventTarget]: https://dom.spec.whatwg.org/#interface-eventtarget
[`--trace-warnings`]: cli.html#cli_trace_warnings
[`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners
Expand Down
114 changes: 10 additions & 104 deletions lib/internal/event_target.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
'use strict';

const {
ArrayFrom,
Error,
Map,
Object,
Set,
Symbol,
NumberIsNaN,
} = primordials;

const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_EVENT_RECURSION,
ERR_OUT_OF_RANGE,
}
} = require('internal/errors');

const perf_hooks = require('perf_hooks');
const { customInspectSymbol } = require('internal/util');
const { inspect } = require('util');

Expand All @@ -29,11 +24,20 @@ const kTarget = Symbol('kTarget');
const kNewListener = Symbol('kNewListener');
const kRemoveListener = Symbol('kRemoveListener');

let perf_hooks;

function lazyNow() {
if (perf_hooks === undefined)
perf_hooks = require('perf_hooks');
return perf_hooks.performance.now();
}


class Event {
#type = undefined;
#defaultPrevented = false;
#cancelable = false;
#timestamp = perf_hooks.performance.now();
#timestamp = lazyNow();

// Neither of these are currently used in the Node.js implementation
// of EventTarget because there is no concept of bubbling or
Expand Down Expand Up @@ -297,101 +301,6 @@ Object.defineProperties(EventTarget.prototype, {
dispatchEvent: { enumerable: true }
});

class NodeEventTarget extends EventTarget {
static defaultMaxListeners = 10;

#maxListeners = NodeEventTarget.defaultMaxListeners;
#maxListenersWarned = false;

[kNewListener](size, type, listener, once, capture, passive) {
if (this.#maxListeners > 0 &&
size > this.#maxListeners &&
!this.#maxListenersWarned) {
this.#maxListenersWarned = true;
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
const w = new Error('Possible EventTarget memory leak detected. ' +
`${size} ${type} listeners ` +
`added to ${inspect(this, { depth: -1 })}. Use ` +
'setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.target = this;
w.type = type;
w.count = size;
process.emitWarning(w);
}
}

setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n);
}
this.#maxListeners = n;
return this;
}

getMaxListeners() {
return this.#maxListeners;
}

eventNames() {
return ArrayFrom(this[kEvents].keys());
}

listenerCount(type) {
const root = this[kEvents].get(String(type));
return root !== undefined ? root.size : 0;
}

off(type, listener, options) {
this.removeEventListener(type, listener, options);
return this;
}

removeListener(type, listener, options) {
this.removeEventListener(type, listener, options);
return this;
}

on(type, listener) {
this.addEventListener(type, listener);
return this;
}

addListener(type, listener) {
this.addEventListener(type, listener);
return this;
}

once(type, listener) {
this.addEventListener(type, listener, { once: true });
return this;
}

removeAllListeners(type) {
if (type !== undefined) {
this[kEvents].delete(String(type));
} else {
this[kEvents].clear();
}
}
}

Object.defineProperties(NodeEventTarget.prototype, {
setMaxListeners: { enumerable: true },
getMaxListeners: { enumerable: true },
eventNames: { enumerable: true },
listenerCount: { enumerable: true },
off: { enumerable: true },
removeListener: { enumerable: true },
on: { enumerable: true },
addListener: { enumerable: true },
once: { enumerable: true },
removeAllListeners: { enumerable: true },
});

// EventTarget API

function validateListener(listener) {
if (typeof listener === 'function' ||
(listener != null &&
Expand Down Expand Up @@ -432,10 +341,7 @@ function emitUnhandledRejectionOrErr(that, err, event) {
process.emit('error', err, event);
}

// EventEmitter-ish API:

module.exports = {
Event,
EventTarget,
NodeEventTarget,
};
Loading