Chrome 扩展消息传递:未发送响应

IT技术 javascript google-chrome google-chrome-extension google-chrome-app
2021-01-14 01:17:12

我正在尝试在内容脚本和扩展程序之间传递消息

这是我在内容脚本中的内容

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函数代码中的端口为空

3个回答

文档中chrome.runtime.onMessage.addListener

当事件侦听器返回时,此函数将无效,除非您从事件侦听器返回 true 以指示您希望异步发送响应(这将使消息通道对另一端保持打开状态,直到调用 sendResponse)。

所以你只需要return true;在调用后加上getUrlsto 表示你将异步调用响应函数。

这是正确的,我在答案中添加了一种自动执行此操作的方法
2021-03-29 01:17:12
我发誓这是我用过的最不直观的 API。
2021-03-30 01:17:12
+1 为此。在浪费了 2 天试图调试这个问题之后,它救了我。我不敢相信这在消息传递指南中根本没有提到:developer.chrome.com/extensions/messaging
2021-04-06 01:17:12
我以前显然遇到过这个问题;回来意识到我已经赞成这个。这需要以粗体显示<blink><marquee>在页面上的某处标记。
2021-04-06 01:17:12
@funforums 仅供参考,这种行为现在记录在消息传递文档中(区别在这里:codereview.chromium.org/1874133002/patch/80001/90002)。
2021-04-06 01:17:12

接受的答案是正确的,我只想添加简化此操作的示例代码。问题是 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;
    }

});

无论您选择如何处理消息,这都会自动处理返回值。请注意,这假设您永远不会忘记调用响应函数。另请注意,铬可以为我们自动执行此操作,我不明白他们为什么没有。

@RobW 但有什么问题呢?我的回答可以防止开发人员忘记返回 true。
2021-03-12 01:17:12
一个问题是有时您不想调用响应函数,在这些情况下您应该返回false否则,您将阻止 Chrome 释放与该消息关联的资源。
2021-03-19 01:17:12
是的,这就是为什么我说不要忘记调用回调。您提到的那种特殊情况可以通过约定处理程序(handleMethod1 等)返回 false 来指示“无响应”情况(尽管 Id 只是总是做出响应,甚至是空响应)来处理。这样,可维护性问题仅局限于那些特殊的“不返回”情况。
2021-03-21 01:17:12
@ZigMandel 如果要发送响应,只需使用return true;. 如果调用是同步的,而异步调用仍在正确处理,则它不会阻止端口被清理。此答案中的代码引入了不必要的复杂性,但没有明显的好处。
2021-04-04 01:17:12
不要重新发明轮子。不推荐使用的chrome.extension.onRequest/chrome.exension.sendRequest方法的行为与您描述的完全相同。这些方法已被弃用,因为事实证明,许多扩展开发人员并未关闭消息端口。当前的 API(需要return true)是一个更好的设计,因为硬失败比静默泄漏要好。
2021-04-05 01:17:12

您可以使用我的库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 }); });
}) );