来自 browser.runtime.sendMessage() 的承诺在异步调用 sendResponse() 之前实现

Promise from browser.runtime.sendMessage() fulfilling prior to asynchronous call to sendResponse()

我制作了一个与可执行文件交互的 Firefox WebExtension(使用 runtime.connectNative)。

这是扩展的清单:

// manifest.json file

{
  "name": "My Extension",
  "short_name": "myext",
  "version": "2.7",
  "manifest_version": 2,
  "background": {
    "scripts": ["background-script.js"],
    "persistent": true
    },
    "externally_connectable": {
      "ids": ["*"],
      "http://localhost/*"]
  },
   "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content-script.js"]
    }
  ],
  "applications": {
    "gecko": {
      "id": "myext@myext.mysite.com",
      "strict_min_version": "50.0.0"
    }
  },
  "icons": {
    "16": "icon-16.png",
    "32": "icon-32.png",
    "128": "icon-128.png"
  },
  "permissions": [
       "nativeMessaging",
       "webRequest"
    ]
}

这是内容脚本:

// content-script.js

window.addEventListener("message", function(event) {
  if (event.source == window &&
      event.data.direction &&
      event.data.direction == "from-page-script") {
    alert("Content script received message: \"" + JSON.stringify(event.data.message) + "\"");

    var sending = browser.runtime.sendMessage(event.data.message);

    sending.then(handleResponse, handleError);

  }
});

function handleResponse(message) 
{
  console.log("content-script : handleResponse : message = " + JSON.stringify(message));
  window.wrappedJSObject.foo.p = "OK : " + JSON.stringify(message);
}

function handleError(error) {
  console.log("Message from the background script : " + error.message);
  window.wrappedJSObject.foo.p = "error";
}

这是我的背景脚本:

// background-script.js

var port;

browser.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    port = browser.runtime.connectNative("ping_pong");
    var result = port.postMessage(request);

    port.onMessage.addListener(function(response)
    {
        console.log("Received: " + JSON.stringify(response));
        sendResponse(response);
    });
});

我对后台脚本中的以下行有疑问:

sendResponse(response);

内容脚本的方法handleResponse()在后台脚本的sendResponse()之前调用。

因此,当可执行文件花费较长时间执行操作时,可执行文件的结果不会发送到内容脚本,内容脚本会收到未定义的结果。

是否有其他方法可以将可执行文件的结果从后台脚本发送到内容脚本?或者以其他方式使用回调?

Promises 的使用在这里引起了一些混乱。你的 handleResponse() 被调用时 Promise fulfilled with no arguments 因为你没有调用 sendResponse() 就退出了后台脚本的 runtime.onMessage 侦听器。如果调用了 sendResponse(),则参数将是消息 1。因此,您需要调整 .then() 以区分不接收参数(sendResponse() 未调用)和包含响应消息的参数(sendResponse() 调用)。如果我没记错的话,如果你使用 chrome.runtime.sendMessage(),你的 responseCallback 函数将不会被调用,除非调用 sendResponse()

MDN describes the Promise as:

If the sender sent a response, this will be fulfilled with the response as a JSON object. Otherwise it will be fulfilled with no arguments. If an error occurs while connecting to the extension, the promise will be rejected with an error message.

要异步调用 sendResponse(),您需要从 runtime.onMessage 侦听器 return true;

您正在从异步回调中调用 sendResponse()。结果,您在调用 sendResponse() 之前退出了 runtime.onMessage 侦听器。如果你打算这样做,但仍想调用 sendResponse(),你需要从你的 runtime.onMessage 监听器中调用 return true

来自 MDN runtime.onMessage 关于 sendResponse():

This function returns a boolean. It should return true from the event listener if you wish to call sendResponse after the event listener returns.

因此您的代码可以是:

browser.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    port = browser.runtime.connectNative("ping_pong");
    var result = port.postMessage(request);

    port.onMessage.addListener(function portOnMessageListener(response)
    {
        //sendResponse is only valid once. So, if this is how you want to use it, you need
        //  to remove the listener, or in some other way not call sendResponse twice.
        port.onMessage.removeListener(portOnMessageListener);
        console.log("Received: " + JSON.stringify(response));
        sendResponse(response);
    });
    return true;
});

sendResponse()只有效一次

sendResponse() 函数仅对一条消息有效(即每发送一条消息您只能发送一个响应)。如果您希望向内容脚本发送多条消息,则需要以不同的方式进行设置。


  1. 由JSON转换而来的MDN docs state the message is a "JSON object". What they probably mean that it is an Object,用于在消息传输过程中表示。