Расширение Chrome. Передача сообщений: ответ не отправлен.

Я пытаюсь передать сообщения между контентом script и расширением

Вот что я имею в содержании - script

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

И в фоновом режиме script у меня есть

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

}

Теперь, если я отправляю ответ перед вызовом ajax в функции getUrls, ответ отправляется успешно, но в методе успеха вызова ajax, когда я отправляю ответ, он его не отправляет, когда я иду в отладку я вижу, что порт имеет нулевое значение внутри кода для функции sendResponse.

Ответ 1

Из документация для chrome.runtime.onMessage.addListener:

Эта функция становится недействительной, когда возвращается прослушиватель событий, если вы не вернете истину из прослушивателя событий, чтобы указать, что вы хотите отправить ответ асинхронно (это будет поддерживать канал сообщения открытым до другого конца, пока не будет вызван вызов sendResponse).

Поэтому вам просто нужно добавить return true; после вызова getUrls, чтобы указать, что вы вызовете функцию ответа асинхронно.

Ответ 2

Принятый ответ правильный, я просто хотел добавить пример кода, который упростит это. Проблема в том, что API (на мой взгляд) не очень хорошо разработан, потому что он заставляет нас разработчиков знать, обрабатывается ли какое-либо конкретное сообщение async или нет. Если вы обрабатываете много разных сообщений, это становится невыполнимой задачей, потому что вы никогда не знаете, если в какой-то степени в какой-то функции переданный метод sendResponse будет вызываться как асинхронный или нет. Рассмотрим это:

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

Как я могу узнать, будет ли в самом низу handleMethod1 вызов асинхронным или нет? Как может кто-то, кто изменяет handleMethod1, знает, что он сломает вызывающего абонента, введя что-то async?

Мое решение таково:

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

});

Это автоматически обрабатывает возвращаемое значение, независимо от того, как вы решите обработать сообщение. Обратите внимание, что это предполагает, что вы никогда не забудете вызвать функцию ответа. Также обратите внимание, что хром мог автоматизировать это для нас, я не понимаю, почему они этого не сделали.

Ответ 3

Вы можете использовать мою библиотеку https://github.com/lawlietmester/webextension, чтобы сделать эту работу как в Chrome, так и в FF с помощью Firefox без обратных вызовов.

Ваш код будет выглядеть так:

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