Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sindresorhus/emittery
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.12.1
Choose a base ref
...
head repository: sindresorhus/emittery
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.13.0
Choose a head ref
  • 2 commits
  • 4 files changed
  • 2 contributors

Commits on Aug 25, 2022

  1. Properly clean up listener storage (#103)

    Co-authored-by: Sindre Sorhus <[email protected]>
    gurupras and sindresorhus authored Aug 25, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3641e7a View commit details
  2. 0.13.0

    sindresorhus committed Aug 25, 2022
    Copy the full SHA
    c402f6c View commit details
Showing with 80 additions and 21 deletions.
  1. +56 −20 index.js
  2. +9 −0 maps.js
  3. +1 −1 package.json
  4. +14 −0 test/index.js
76 changes: 56 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use strict';

const anyMap = new WeakMap();
const eventsMap = new WeakMap();
const producersMap = new WeakMap();
const {anyMap, producersMap, eventsMap} = require('./maps.js');

const anyProducer = Symbol('anyProducer');
const resolvedPromise = Promise.resolve();

@@ -28,7 +27,7 @@ function assertListener(listener) {
function getListeners(instance, eventName) {
const events = eventsMap.get(instance);
if (!events.has(eventName)) {
events.set(eventName, new Set());
return;
}

return events.get(eventName);
@@ -38,7 +37,7 @@ function getEventProducers(instance, eventName) {
const key = typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number' ? eventName : anyProducer;
const producers = producersMap.get(instance);
if (!producers.has(key)) {
producers.set(key, new Set());
return;
}

return producers.get(key);
@@ -79,7 +78,14 @@ function iterator(instance, eventNames) {
};

for (const eventName of eventNames) {
getEventProducers(instance, eventName).add(producer);
let set = getEventProducers(instance, eventName);
if (!set) {
set = new Set();
const producers = producersMap.get(instance);
producers.set(eventName, set);
}

set.add(producer);
}

return {
@@ -111,7 +117,14 @@ function iterator(instance, eventNames) {
queue = undefined;

for (const eventName of eventNames) {
getEventProducers(instance, eventName).delete(producer);
const set = getEventProducers(instance, eventName);
if (set) {
set.delete(producer);
if (set.size === 0) {
const producers = producersMap.get(instance);
producers.delete(eventName);
}
}
}

flush();
@@ -221,6 +234,9 @@ class Emittery {
anyMap.set(this, new Set());
eventsMap.set(this, new Map());
producersMap.set(this, new Map());

producersMap.get(this).set(anyProducer, new Set());

this.debug = options.debug || {};

if (this.debug.enabled === undefined) {
@@ -259,7 +275,14 @@ class Emittery {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
getListeners(this, eventName).add(listener);
let set = getListeners(this, eventName);
if (!set) {
set = new Set();
const events = eventsMap.get(this);
events.set(eventName, set);
}

set.add(listener);

this.logIfDebugEnabled('subscribe', eventName, undefined);

@@ -277,7 +300,14 @@ class Emittery {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
getListeners(this, eventName).delete(listener);
const set = getListeners(this, eventName);
if (set) {
set.delete(listener);
if (set.size === 0) {
const events = eventsMap.get(this);
events.delete(eventName);
}
}

this.logIfDebugEnabled('unsubscribe', eventName, undefined);

@@ -321,7 +351,7 @@ class Emittery {

enqueueProducers(this, eventName, eventData);

const listeners = getListeners(this, eventName);
const listeners = getListeners(this, eventName) || new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners];
@@ -350,7 +380,7 @@ class Emittery {

this.logIfDebugEnabled('emitSerial', eventName, eventData);

const listeners = getListeners(this, eventName);
const listeners = getListeners(this, eventName) || new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = [...anyListeners];
@@ -401,28 +431,34 @@ class Emittery {
this.logIfDebugEnabled('clear', eventName, undefined);

if (typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number') {
getListeners(this, eventName).clear();
const set = getListeners(this, eventName);
if (set) {
set.clear();
}

const producers = getEventProducers(this, eventName);
if (producers) {
for (const producer of producers) {
producer.finish();
}

for (const producer of producers) {
producer.finish();
producers.clear();
}

producers.clear();
} else {
anyMap.get(this).clear();

for (const listeners of eventsMap.get(this).values()) {
for (const [eventName, listeners] of eventsMap.get(this).entries()) {
listeners.clear();
eventsMap.get(this).delete(eventName);
}

for (const producers of producersMap.get(this).values()) {
for (const [eventName, producers] of producersMap.get(this).entries()) {
for (const producer of producers) {
producer.finish();
}

producers.clear();
producersMap.get(this).delete(eventName);
}
}
}
@@ -434,8 +470,8 @@ class Emittery {

for (const eventName of eventNames) {
if (typeof eventName === 'string') {
count += anyMap.get(this).size + getListeners(this, eventName).size +
getEventProducers(this, eventName).size + getEventProducers(this).size;
count += anyMap.get(this).size + (getListeners(this, eventName) || new Set()).size +
(getEventProducers(this, eventName) || new Set()).size + (getEventProducers(this) || new Set()).size;
continue;
}

9 changes: 9 additions & 0 deletions maps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const anyMap = new WeakMap();
const eventsMap = new WeakMap();
const producersMap = new WeakMap();

module.exports = {
anyMap,
eventsMap,
producersMap
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "emittery",
"version": "0.12.1",
"version": "0.13.0",
"description": "Simple and modern async event emitter",
"license": "MIT",
"repository": "sindresorhus/emittery",
14 changes: 14 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import test from 'ava';
import delay from 'delay';
import pEvent from 'p-event';
import Emittery from '../index.js';
import {eventsMap} from '../maps.js';

test('on()', async t => {
const emitter = new Emittery();
@@ -348,6 +349,19 @@ test('off() - no listener', t => {
}, TypeError);
});

test('off() - clears global maps when all listeners are removed', t => {
const emitter = new Emittery();

const event = 'string';
const callback = () => {};

emitter.on(event, callback);
t.is(eventsMap.get(emitter).get(event).size, 1);

emitter.off(event, callback);
t.is(eventsMap.get(emitter).get(event), undefined);
});

test('once()', async t => {
const fixture = '🌈';
const emitter = new Emittery();