Chrome Extension: Code inside chrome.runtime.onMessage.addListener never gets executed

Viewed 350

I'm having a lot of trouble trying to pass a variable from a content script to the popup.js using the sendMessage method.

This is the content script where I'm sending a message to the background script that contains the number of children of a DOM node:

let incomingChatsNumber = incomingChatsContainer.children().length;
chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });

Then the background.js listen to the message and send a messag itself with the same variable to the popup.js:

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  let incomingChatsNumber = message.incomingChatsNumber;
  chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
});

In the popup.js I have a button that will trigger some code if the "incomingChatsNumber" is more than 0 (the dom container has children):

  $("#js-toggleSorting").on("click", function () {
    chrome.runtime.onMessage.addListener(function (message) {
      let incomingChatsNumber = message.incomingChatsNumber;
      if (incomingChatsNumber <= 0) {
        $(".message").html("<p>No Chats To Sort, You Joker</p>");
      } else {
        $(".message").html("<p>Sorting Chats</p>");
        //This code never gets executed
        if ($("#js-toggleSorting").attr("data-click-state") == 1) {
          $("#js-toggleSorting").attr("data-click-state", 0);
          $("#js-toggleSorting").html("SORT INCOMING CHATS");
          sortFunction(false);
        } else {
          $("#js-toggleSorting").attr("data-click-state", 1);
          $("#js-toggleSorting").html("STOP SORTING INCOMING CHATS");
          sortFunction(true);
        }
        save_button_state();
      }
    });
  });

The strange thing for me is that the popup.js gets the value right I can even console.log it, but as soon as I put more code on that if conditional the code never gets executed even when the condition is ok to go.

UPDATE:

After doing the modifications suggested by ehab I realized that the variable is "undefined" all the time because of the async nature of chrome.runtime.onMessage.addListener:

Content Script:

  let incomingChatsNumber = incomingChatsContainer.children().length;
  chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });

Background.js:

chrome.runtime.onMessage.addListener(function (message) {
  let incomingChatsNumber = message.incomingChatsNumber;
  chrome.runtime.sendMessage({ incomingChatsNumber: incomingChatsNumber });
});

Popup.js (This is where I need to use the value of the incomingChatsNumber variable):

  let latestIncomingChatsNumber;

  chrome.runtime.onMessage.addListener(function (message) {
    let incomingChatsNumber = message.incomingChatsNumber;
    latestIncomingChatsNumber = incomingChatsNumber;
    //This Works
    console.log(latestIncomingChatsNumber);
  });
  //This will give me an undefined value
  console.log(latestIncomingChatsNumber);

I get that my problem is related to the fact that the chrome.* APIs are asynchronous so The chrome.runtime... and the console.log(...) will be executed at the same time, hence the error. So how to use the latestIncomingChatsNumber variable inside an on click event then ? Do I have to to save "latestIncomingChatsNumber" to the local storage inside the chrome.runtime.onMessage.addListener first ?

1 Answers

You should not put an event listener inside another event handler, this is an extremely bad practice even if it do what you think it should be doing, this will cause memory leaks for you down the road, and makes the code hard to read and semantically incorrect.

It seems based on looking at your code the problem is that the handler for the message gets called multiple times because of previous event listeners added in the click evet

 let latestIncomingChatsNumber
 chrome.runtime.onMessage.addListener(function (message) {
      let incomingChatsNumber = message.incomingChatsNumber;
      // save this into the application state, i will use a parent scope variable you could use something else 
       latestIncomingChatsNumber = incomingChatsNumber
    });
    
 $("#js-toggleSorting").on("click", function () {
      if (latestIncomingChatsNumber <= 0) {
        $(".message").html("<p>No Chats To Sort, You Joker</p>");
      } else {
        $(".message").html("<p>Sorting Chats</p>");
        //This code never gets executed
        if ($("#js-toggleSorting").attr("data-click-state") == 1) {
          $("#js-toggleSorting").attr("data-click-state", 0);
          $("#js-toggleSorting").html("SORT INCOMING CHATS");
          sortFunction(false);
        } else {
          $("#js-toggleSorting").attr("data-click-state", 1);
          $("#js-toggleSorting").html("STOP SORTING INCOMING CHATS");
          sortFunction(true);
        }
        save_button_state(); // i believe this function saves the attributes to the dom elements
      }
  });

if save_button_state does a good job the code i shared should work

Related