Chrome拡張メッセージの受け渡し:応答が送信されていません


151

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

これは私がコンテンツスクリプトに持っているものです

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関数のコード内ではポートはnull です。


sendResponseパラメータへの参照を保存することは重要です。これがないと、応答オブジェクトはスコープ外になり、呼び出すことができません。私の問題を修正するように私をほのめかしたコードをありがとう!
TrickiDicki

多分別の解決策は、Promiseで非同期関数内にすべてをラップし、非同期メソッドを待機することです。
エンリケ

回答:


348

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

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

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


これは正しいです。これを自動化する方法を私の答えに追加しました
Zig Mandel

62
+1。この問題をデバッグするために2日間を費やした後、それは私を救いました。これは、メッセージパッシングガイドのdeveloper.chrome.com/extensions/messaging
funforums 2014年

6
私は明らかにこの問題を以前に経験したことがあります。私はすでにこれに賛成していたことに気づきました。これは、ニーズビッグで大胆になるように<blink>し、<marquee>ページ上のタグのどこか。
Qix-モニカは2015

2
@funforums FYI、この動作は現在メッセージングのドキュメントに記載されています(違いはここにあります:codereview.chromium.org/1874133002/patch/80001/90002)。
Rob W

10
これが今まで使用した中で最も直感的でないAPIであることを私は誓います。
マイケルズナウデン2016

8

受け入れられた答えは正しいです。私はこれを単純化するサンプルコードを追加したかっただけです。問題は、特定のメッセージが非同期で処理されるかどうかを開発者に知らせなければならないため、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;
    }

});

これにより、メッセージの処理方法の選択に関係なく、戻り値が自動的に処理されます。これは、応答関数の呼び出しを忘れないことを前提としています。また、クロムがこれを自動化していた可能性があることにも注意してください。なぜそうしなかったのかわかりません。


1つの問題は、応答関数を呼び出さないことがあり、その場合はfalseを返す必要があることです。そうしないと、Chromeがメッセージに関連付けられているリソースを解放できなくなります。
rsanchez 2014年

はい、それが私がコールバックの呼び出しを忘れないように言った理由です。ハンドラー(handleMethod1など)がfalseを返して "応答なし"のケースを示すという慣例を設けることで、言及する特別なケースを処理できます(Idは、常に空のケースであっても、常に応答を返します)。このようにして、保守性の問題は、これらの特別な「ノーリターン」の場合のみに限定されます。
ジグマンデル2014年

8
車輪を再発明しないでください。非推奨のchrome.extension.onRequest/ chrome.exension.sendRequestメソッドは、ユーザーが説明したとおりに動作します。多くの拡張機能開発者がメッセージポートを閉じなかったことが判明したため、これらのメソッドは推奨されません。現在のAPI(が必要return true)は優れた設計です。ハードに失敗することは、静かにリークするよりも優れているからです。
Rob W

@RobWでも問題は何ですか?私の答えは、開発者がtrueを返すのを忘れないようにします。
ジグマンデル2015

@ZigMandel応答を送信する場合は、単にを使用しますreturn true;。非同期の呼び出しがまだ正しく処理されている間、呼び出しが同期している場合、ポートがクリーンアップされるのを防ぎません。この回答のコードは不必要な複雑さをもたらし、明らかな利点はありません。
Rob W

2

私のライブラリ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 }); });
}) );
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.