【问题标题】:Chrome Extension Message passing: response not sentChrome 扩展消息传递:未发送响应
【发布时间】:2013-11-19 17:00:00
【问题描述】:

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

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

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

【问题讨论】:

  • 存储对 sendResponse 参数的引用至关重要。没有它,响应对象将超出范围并且无法调用。感谢提示我解决问题的代码!
  • 也许另一种解决方案是使用 Promise 将所有内容包装在异步函数中并为异步方法调用 await?

标签: javascript google-chrome google-chrome-extension google-chrome-app


【解决方案1】:

来自the documentation for chrome.runtime.onMessage.addListener

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

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

【讨论】:

  • 这是正确的,我在答案中添加了一种自动化方法
  • 为此 +1。在浪费了 2 天时间尝试调试此问题后,它救了我。我不敢相信在消息传递指南中根本没有提到这一点:developer.chrome.com/extensions/messaging
  • 我以前显然遇到过这个问题;回来意识到我已经对此表示赞同。这需要在页面某处的大 <blink><marquee> 标记中以粗体显示。
  • @funforums 仅供参考,此行为现在记录在 messaging 文档中(区别在这里:codereview.chromium.org/1874133002/patch/80001/90002)。
  • 我发誓这是我用过的最不直观的 API。
【解决方案2】:

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

});

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

【讨论】:

  • 一个问题是有时你不想调用响应函数,在这种情况下你应该返回false。否则,您将阻止 Chrome 释放与邮件相关的资源。
  • 是的,这就是为什么我说不要忘记调用回调。您提到的那种特殊情况可以通过约定处理程序(handleMethod1 等)返回 false 以指示“无响应”情况(尽管 Id 总是做出响应,即使是空的)来处理。这样,可维护性问题只局限于那些特殊的“不归路”情况。
  • 不要重新发明轮子。已弃用的 chrome.extension.onRequest / chrome.exension.sendRequest 方法的行为与您描述的完全一样。这些方法已被弃用,因为事实证明许多扩展开发人员没有关闭消息端口。当前的 API(需要 return true)是更好的设计,因为硬失败比无声泄漏要好。
  • @RobW 但那是什么问题呢?我的回答可以防止开发人员忘记返回 true。
  • @ZigMandel 如果您想发送回复,只需使用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 }); });
}) );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2021-07-27
    • 1970-01-01
    相关资源
    最近更新 更多