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

Tho*_*dax 1 javascript firefox-addon firefox-addon-webextensions

我制作了一个与可执行文件交互的 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"
    ]
}
Run Code Online (Sandbox Code Playgroud)

这是内容脚本:

// 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";
}
Run Code Online (Sandbox Code Playgroud)

这是我的背景脚本:

// 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);
    });
});
Run Code Online (Sandbox Code Playgroud)

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

sendResponse(response);
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mak*_*yen 5

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

MDN 将 Promise 描述为

如果发送方发送了响应,这将通过作为 JSON 对象的响应来实现。否则它将在没有参数的情况下实现。如果在连接到扩展时发生错误,promise 将被拒绝并显示错误消息。

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

sendResponse()从异步回调中调用。因此,您runtime.onMessagesendResponse()被调用之前退出侦听器。如果您打算这样做,并且仍想调用sendResponse(),则需要trueruntime.onMessage侦听器返回。

来自MDNruntime.onMessage关于sendResponse()

此函数返回一个boolean. true如果您希望sendResponse在事件侦听器返回后调用,它应该从事件侦听器返回。

所以你的代码可能是:

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;
});
Run Code Online (Sandbox Code Playgroud)

sendResponse() 仅有效一次

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


  1. MDN文档状态的消息是“JSON对象”。他们可能的意思是,它是一个从 JSON 转换而来的对象,用于在消息传输时表示它。