Transferência de mensagem de extensão do Chrome: resposta não enviada

Estou tentando passar mensagens entre o script de conteúdo e a extensão

Aqui está o que eu tenho no script de conteúdo

chrome.runtime.sendMessage({type: "getUrls"}, function(response) { console.log(response) }); 

E no script de fundo eu tenho

 chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { if (request.type == "getUrls"){ getUrls(request, sender, sendResponse) } }); function getUrls(request, sender, sendResponse){ var resp = sendResponse; $.ajax({ url: "http://localhost:3000/urls", method: 'GET', success: function(d){ resp({urls: d}) } }); } 

Agora, se eu enviar a resposta antes da chamada do ajax na function getUrls , a resposta é enviada com sucesso, mas no método de sucesso da chamada do ajax, quando envio a resposta, ela não é enviada. Quando entro na debugging, posso ver que a porta é nula dentro do código para a function sendResponse .

Na documentação do chrome.runtime.onMessage.addListener :

Essa function se torna inválida quando o listener de events retorna, a menos que você retorne true do listener de events para indicar que deseja enviar uma resposta de forma assíncrona (isso manterá o canal de mensagem aberto para a outra extremidade até que o sendResponse seja chamado).

Então você só precisa adicionar o return true; após a chamada para getUrls para indicar que você chamará a function de resposta de forma assíncrona.

A resposta aceita está correta, eu só queria adicionar código de exemplo que simplifica isso. O problema é que a API (na minha opinião) não é bem projetada porque nos força os desenvolvedores a saber se uma mensagem específica será tratada como assíncrona ou não. Se você lida com muitas mensagens diferentes, isso se torna uma tarefa impossível, porque você nunca sabe se, no fundo, alguma function de um sendResponse transmitido será chamada assíncrona ou não. Considere isto:

 chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) { if (request.method == "method1") { handleMethod1(sendResponse); } 

Como posso saber se no fundo handleMethod1 a chamada será assíncrona ou não? Como alguém que modifica o handleMethod1 sabe que ele irá quebrar um chamador introduzindo algo asynchronous?

Minha solução é esta:

 chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) { var responseStatus = { bCalled: false }; function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async try { sendResponseParam(obj); } catch (e) { //error handling } responseStatus.bCalled= true; } if (request.method == "method1") { handleMethod1(sendResponse); } else if (request.method == "method2") { handleMethod2(sendResponse); } ... if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is. return true; } }); 

Isso manipula automaticamente o valor de retorno, independentemente de como você escolhe manipular a mensagem. Observe que isso pressupõe que você nunca se esqueça de chamar a function de resposta. Observe também que o cromo poderia ter automatizado isso para nós, não vejo por que não o fizeram.

Você pode usar minha biblioteca https://github.com/lawlietmester/webextension para fazer isso funcionar tanto no Chrome quanto no FF com o Firefox, sem retornos de chamada.

Seu código será parecido com:

 Browser.runtime.onMessage.addListener( request => new Promise( resolve => { if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return; $.ajax({ 'url': "http://localhost:3000/urls", 'method': 'GET' }).then( urls => { resolve({ urls }); }); }) );