web-dev-qa-db-ja.com

Chrome拡張メッセージの受け渡し:応答は送信されませんでした

コンテンツスクリプトと拡張機能の間でメッセージをやり取りしようとしています

ここに私がコンテンツスクリプトに持っているものがあります

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

バックグラウンドスクリプトには

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

}

getUrls関数でajax呼び出しの前に応答を送信すると、応答は正常に送信されますが、応答を送信するときのajax呼び出しの成功メソッドでは、応答を送信しません。デバッグsendResponse関数のコード内でポートがヌルであることがわかります。

144
Abid

chrome.runtime.onMessage.addListenerのドキュメント から:

非同期で応答を送信することを示すためにイベントリスナーからtrueを返さない限り、イベントリスナーが戻ると、この関数は無効になります(これにより、sendResponseが呼び出されるまでメッセージチャネルが相手側に開かれたままになります)。

したがって、getUrlsへの呼び出しの後にreturn true;を追加して、応答関数を非同期に呼び出すことを示す必要があります。

331
rsanchez

受け入れられた答えは正しいです。これを単純化するサンプルコードを追加したかっただけです。問題は、特定のメッセージが非同期で処理されるかどうかを開発者に知らせるため、API(私の見解では)が適切に設計されていないことです。さまざまなメッセージを処理する場合、これは不可能なタスクになります。これは、渡されたsendResponseの一部の関数が非同期と呼ばれるかどうかがわからないためです。このことを考慮:

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

handleMethod1呼び出しが非同期であるかどうかを知るにはどうすればよいですか? handleMethod1を変更する人は、非同期のものを導入することで発信者を中断することをどのようにして知ることができますか?

私の解決策はこれです:

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

});

これは、メッセージの処理方法に関係なく、戻り値を自動的に処理します。これは、応答関数の呼び出しを忘れないことを前提としていることに注意してください。また、クロムは私たちのためにこれを自動化できたかもしれないことに注意してください、彼らがそうしなかった理由はわかりません。

8
Zig Mandel

私のライブラリ https://github.com/lawlietmester/webextension を使用して、コールバックなしでFirefoxの方法でChromeとFFの両方でこの作業を行うことができます。

コードは次のようになります。

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