Bug 1603035 - Return a function from EventEmitter.on that removes the event listener when called. r=rcaliman.
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Fri, 13 Dec 2019 13:55:23 +0000
changeset 507116 e81d6fea3194170db04ccd828ecfcbf1b35c545a
parent 507115 371668448156733495b2a452f719446ec2317c05
child 507117 b043364b163656cc4989626ee51aa81add710cd1
push id36923
push userccoroiu@mozilla.com
push dateMon, 16 Dec 2019 21:47:33 +0000
treeherdermozilla-central@c238b47d7d57 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcaliman
bugs1603035
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1603035 - Return a function from EventEmitter.on that removes the event listener when called. r=rcaliman. This will be helpful when consumers don't want to keep the target around. A test is added to ensure this work as expected (and was failing if the returned function does not call EventEmitter.off). Differential Revision: https://phabricator.services.mozilla.com/D56685
devtools/shared/event-emitter.js
devtools/shared/tests/unit/test_eventemitter_basic.js
--- a/devtools/shared/event-emitter.js
+++ b/devtools/shared/event-emitter.js
@@ -23,16 +23,18 @@ class EventEmitter {
    * specified `type` is emitted on the given event `target`.
    *
    * @param {Object} target
    *    Event target object.
    * @param {String} type
    *    The type of event.
    * @param {Function|Object} listener
    *    The listener that processes the event.
+   * @returns {Function}
+   *    A function that removes the listener when called.
    */
   static on(target, type, listener) {
     if (typeof listener !== "function" && !isEventHandler(listener)) {
       throw new Error(BAD_LISTENER);
     }
 
     if (!(eventListeners in target)) {
       target[eventListeners] = new Map();
@@ -40,16 +42,18 @@ class EventEmitter {
 
     const events = target[eventListeners];
 
     if (events.has(type)) {
       events.get(type).add(listener);
     } else {
       events.set(type, new Set([listener]));
     }
+
+    return () => EventEmitter.off(target, type, listener);
   }
 
   /**
    * Removes an event `listener` for the given event `type` on the given event
    * `target`. If no `listener` is passed removes all listeners of the given
    * `type`. If `type` is not passed removes all the listeners of the given
    * event `target`.
    * @param {Object} target
@@ -248,17 +252,17 @@ class EventEmitter {
     return Object.defineProperties(target, descriptors);
   }
 
   static get handler() {
     return handler;
   }
 
   on(...args) {
-    EventEmitter.on(this, ...args);
+    return EventEmitter.on(this, ...args);
   }
 
   off(...args) {
     EventEmitter.off(this, ...args);
   }
 
   clearEvents() {
     EventEmitter.clearEvents(this);
--- a/devtools/shared/tests/unit/test_eventemitter_basic.js
+++ b/devtools/shared/tests/unit/test_eventemitter_basic.js
@@ -250,16 +250,31 @@ const TESTS = {
     equal(received.length, 3, "the listener was triggered three times");
 
     emitter.clearEvents();
     emitter.emit("a", 1);
     emitter.emit("b", 1);
     emitter.emit("c", 1);
     equal(received.length, 3, "the listener was not called after clearEvents");
   },
+
+  testOnReturn() {
+    const emitter = getEventEmitter();
+
+    let called = false;
+    const removeOnTest = emitter.on("test", () => {
+      called = true;
+    });
+
+    equal(typeof removeOnTest, "function", "`on` returns a function");
+    removeOnTest();
+
+    emitter.emit("test");
+    equal(called, false, "event listener wasn't called");
+  },
 };
 
 /**
  * Create a runnable tests based on the tests descriptor given.
  *
  * @param {Object} tests
  *  The tests descriptor object, contains the tests to run.
  */