Extensão do Chrome: o código dentro de chrome.runtime.onMessage.addListener nunca é executado

Aug 18 2020

Estou tendo muitos problemas ao tentar passar uma variável de um script de conteúdo para o popup.js usando o método sendMessage.

Este é o script de conteúdo onde estou enviando uma mensagem para o script de segundo plano que contém o número de filhos de um nó DOM:

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

Então o background.js escuta a mensagem e envia uma mensagem ele mesmo com a mesma variável para o popup.js:

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

No popup.js tenho um botão que acionará algum código caso o "incomingChatsNumber" seja maior que 0 (o container dom tem filhos):

  $("#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();
      }
    });
  });

O estranho para mim é que o popup.js obtém o valor correto, posso até console.log, mas assim que coloco mais código nisso, se condicional, o código nunca é executado, mesmo quando a condição está ok.

ATUALIZAR:

Depois de fazer as modificações sugeridas pelo ehab, percebi que a variável está "indefinida" o tempo todo devido à natureza assíncrona de chrome.runtime.onMessage.addListener:

Roteiro de conteúdo:

  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 (é aqui que eu preciso usar o valor da variável entranteChatsNumber):

  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);

Entendo que meu problema está relacionado ao fato de que as APIs chrome.* são assíncronas, então o chrome.runtime... e o console.log(...) serão executados ao mesmo tempo, daí o erro. Então, como usar a variável lastIncomingChatsNumber dentro de um evento de clique? Preciso salvar "latestIncomingChatsNumber" no armazenamento local dentro do chrome.runtime.onMessage.addListener primeiro?

Respostas

1 ehab Aug 18 2020 at 21:41

Você não deve colocar um ouvinte de eventos dentro de outro manipulador de eventos, esta é uma prática extremamente ruim, mesmo que faça o que você acha que deveria estar fazendo, isso causará vazamentos de memória para você no futuro e tornará o código difícil de ler e semanticamente incorreta.

Parece que, com base na observação do seu código, o problema é que o manipulador da mensagem é chamado várias vezes devido a ouvintes de eventos anteriores adicionados no 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
      }
  });

se save_button_statefizer um bom trabalho, o código que compartilhei deve funcionar