Removing an onMessage Listener

I’ve done some message passing to prevent my code from executing while it is waiting for user input.

One small problem is that the listener is not being removed, so the second time, I am getting the information pasted into the text box twice.

I am probably missing something in the syntax for removeListener.

Here is my (fabulously ugly) code:

browser.menus.onClicked.addListener((info, tab, defaultMenu) => {
  if (info.menuItemId.substring(0, 6) == "bbcwbx") {
    for (i = 0; i < defMenu.length; i++) {
      if (info.menuItemId == defMenu[i].menuId) {
        var clickArg = defMenu[i].menuArg;
        if (clickArg.includes("{{zzGetColor")){
          let moo = browser.browserAction.openPopup();
          console.log(moo);
          browser.runtime.onMessage.addListener(function(message, sender) {
          console.log("message received")
          browser.tabs.sendMessage(tab.id, clickArg); // send argument to content script for execution
        });
          browser.runtime.onMessage.removeListener(function(message, sender ){});
         } else {
        browser.tabs.sendMessage(tab.id, clickArg); // send argument to content script for execution
        }
      }
    }
}});

The arguments for both the add and remove listener are the same, so the remove listener should remove the add listener, but it isn’t.

You are using function literals. A literal is a new value every time that piece of code is executed. To remove a listener you need to pass a reference to the same function that you added a listener to, so it’d have to look more like:

function myListener(message, sender) {}
browser.runtime.onMessage.addListener(myListener);
browser.runtime.onMessage.removeeListener(myListener);

or

const myFunction = function(message, sender) {};
browser.runtime.onMessage.addListener(myFunction);
browser.runtime.onMessage.removeListener(myFunction);

Also don’t ever use var, especially in the for loops. It can have some serious consequences…
If you don’t believe me, try to guess what will print this code :smiley:

for (let i = 0; i < 10; i++) { 
  var x = i; 
  setTimeout(() => console.log(x, i));  
}

Stick with the const and if you really need to mutate variable, use let.

1 Like

How can you use a const in a for loop?

Isn’t a constant by definition constant, and so it cannot be used as a counter?

Also, I did change my vars to let on the working code:

browser.menus.onClicked.addListener((info, tab, defaultMenu) => {
 if (info.menuItemId.substring(0, 6) == "bbcwbx") {
  for (let i = 0; i < defMenu.length; i++) {
   if (info.menuItemId == defMenu[i].menuId) {
    var clickArg = defMenu[i].menuArg;
    if (clickArg.includes("{{zzGetColor")){
     browser.browserAction.openPopup();
     browser.runtime.onMessage.addListener(meepMoopMop = function(message, sender) {
     console.log("message received")
     browser.tabs.sendMessage(tab.id, clickArg); 
     browser.runtime.onMessage.removeListener(meepMoopMop);
    });
    } else {
    browser.tabs.sendMessage(tab.id, clickArg); 
    }
   }
  }
}});

Fugly, I know, but I comment extensively for readability.

With a for…of or for…in loop where the value is rebound every iteration step.

I mean in the body of the for loop, for example this one:

var clickArg = defMenu[i].menuArg;  // should be const

Also for a simple array iteration I would avoid using for loop and use .forEach (or .map if you need a result) instead.
Since JavaScript variables has a function scope, you wouldn’t have the same issue with var.
For example:

Array(10).fill().forEach((_, i) => {
  var x = i; 
  setTimeout(() => console.log(x, i));  
})

Oh. I was assuming that constant meant what it did when I first studidied programming. (Fortran)

My bad, and yes, I am old.

It kind of does, but not entirely. const in JS only says that you can not assign a new value to it. However the const in for…of loops is never re-assigned to, which is the entire reason that it’s nicer to use, because you get an entirely new variable every iteration.