jcayzac
jcayzac9mo ago

Migrating from EventEmitter…

Hi! I've got some legacy code that depends on node:events 's EventEmitter class, specifically its once() method that returns a promise resolving the next time a specific event arrives, as well as removeAllListeners(). What would you suggest I use to get rid of that dependency? I can't use EventTarget since it doesn't let you drop all the registered listeners and doesn't support the once() scenario. Anything else?
4 Replies
jcayzac
jcayzac9mo ago
I can make a small wrapper around addEventListener("name", listener, { once: true }) for the promisified once(), but not sure how to do the removeAllListeners() call with EventTarget. In the REPL I can see there is some listeners object under [Symbol()] but I have no idea how to acces it.
Deno AI Helper
Deno AI Helper9mo ago
Based on the documentation, Deno's EventTarget indeed does not provide a removeAllListeners() equivalent or a once() method that returns a promise. However, you can achieve similar functionality with a bit of extra code. For the once() method, you can create a new Promise and resolve it inside the event listener. After the event is fired, you can remove the listener. Here's an example:
function once(target, type) {
return new Promise((resolve) => {
const listener = (event) => {
resolve(event);
target.removeEventListener(type, listener);
};
target.addEventListener(type, listener);
});
}

const target = new EventTarget();
once(target, "foo").then((event) => console.log(event));
target.dispatchEvent(new Event("foo"));
function once(target, type) {
return new Promise((resolve) => {
const listener = (event) => {
resolve(event);
target.removeEventListener(type, listener);
};
target.addEventListener(type, listener);
});
}

const target = new EventTarget();
once(target, "foo").then((event) => console.log(event));
target.dispatchEvent(new Event("foo"));
As for the removeAllListeners() method, there isn't a direct equivalent in the EventTarget interface. However, you can keep track of your listeners and remove them manually when needed. Here's an example:
const listeners = new Map();

function addListener(target, type, listener) {
target.addEventListener(type, listener);
if (!listeners.has(type)) {
listeners.set(type, new Set());
}
listeners.get(type).add(listener);
}

function removeAllListeners(target, type) {
if (listeners.has(type)) {
for (const listener of listeners.get(type)) {
target.removeEventListener(type, listener);
}
listeners.get(type).clear();
}
}

const target = new EventTarget();
addListener(target, "foo", () => console.log("foo"));
addListener(target, "foo", () => console.log("bar"));
removeAllListeners(target, "foo");
const listeners = new Map();

function addListener(target, type, listener) {
target.addEventListener(type, listener);
if (!listeners.has(type)) {
listeners.set(type, new Set());
}
listeners.get(type).add(listener);
}

function removeAllListeners(target, type) {
if (listeners.has(type)) {
for (const listener of listeners.get(type)) {
target.removeEventListener(type, listener);
}
listeners.get(type).clear();
}
}

const target = new EventTarget();
addListener(target, "foo", () => console.log("foo"));
addListener(target, "foo", () => console.log("bar"));
removeAllListeners(target, "foo");
Please note that these are not direct replacements and might not cover all use cases of the original EventEmitter methods. Also, this information is not
Deno AI Helper
Deno AI Helper9mo ago
explicitly stated in the documentation, but it's a general approach to achieve similar functionality. 🦕
jcayzac
jcayzac9mo ago
AI bot has the first example wrong. There's no need to call removeEventListener if you use addEventListener(type, listener, { once: true}). Also this doesn't handle abort signals...