Bug 1603035 - Return a function from EventEmitter.on that removes the event listener when called. r=rcaliman.
☠☠ backed out by 535b50b4c504 ☠ ☠
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Fri, 13 Dec 2019 09:08:11 +0000
changeset 506862 ae02ae71f8463322f59b14087c3e18aa2646a791
parent 506861 2a87eb08886e30ca7bbbc4438220aa5c97a61630
child 506863 1796a39b020bf7b87b1d640b2caea0bf5a953b09
push id103126
push usernchevobbe@mozilla.com
push dateFri, 13 Dec 2019 09:19:49 +0000
treeherderautoland@da1672f831d1 [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.
  */