Extensão do Chrome: o código dentro de chrome.runtime.onMessage.addListener nunca é executado
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
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_state
fizer um bom trabalho, o código que compartilhei deve funcionar