Chrome Extension Message passing: response not sent

JavascriptGoogle ChromeGoogle Chrome-ExtensionGoogle Chrome-App

Javascript Problem Overview


I am trying to pass messages between content script and the extension

Here is what I have in content-script

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

And in the background script I have

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

}

Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.

Javascript Solutions


Solution 1 - Javascript

From the documentation for chrome.runtime.onMessage.addListener: > This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).

So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.

Solution 2 - Javascript

The accepted answer is correct, I just wanted to add sample code that simplifies this. The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not. Consider this:

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

How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?

My solution is this:

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

});

This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.

Solution 3 - Javascript

You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.

Your code will look like:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbidView Question on Stackoverflow
Solution 1 - JavascriptrsanchezView Answer on Stackoverflow
Solution 2 - JavascriptZig MandelView Answer on Stackoverflow
Solution 3 - JavascriptRustamView Answer on Stackoverflow