Port.postmessage() 中的发送响应

sendResponse in Port.postmessage()

我有以下代码

browser.runtime.onConnect.addListener(function (externalPort) {
    externalPort.onMessage.addListener((message, sender, sendResponse) => {
      sendResponse(42);
    }
});

但是,Port.onMessage 的侦听器似乎不会像 browser.runtime.onMessage 的侦听器那样通过 sendResponse 被调用。

知道如何将消息响应发送到端口吗?

Port.postMessage() 是一种仅推送消息传递方法,因此您需要并行使用常规 runtime.sendMessage() 方法。这是一个例子:

manifest.json:

{
  "name": "panel example",
  "version": "1",
  "manifest_version": 2,
  "background": {
    "scripts": ["background.js"]
  },
  "browser_action": {
    "default_title": "panel",
    "default_popup": "panel.html"
  },
  "permissions": [
    "tabs"
  ]
}

background.js:

browser.runtime.onConnect.addListener(port => {
  let tabId;
  const listenerForPort = (message, sender) => {
    if (message &&
        typeof message == 'object' &&
        message.portName == port.name) {
      switch (message.type) {
        case 'get-tabId':
          return Promise.resolve(tabId);
      }
    }
  };
  browser.runtime.onMessage.addListener(listenerForPort);
  port.onMessage.addListener(message => {
    if (message &&
        typeof message == 'object' &&
        message.tabId)
      tabId = message.tabId;
  });
  port.onDisconnect.addListener(port => {
    browser.runtime.onMessage.removeListener(listenerForPort);
    if (tabId)
      browser.tabs.remove(tabId);
  });
});

panel.html:

<!DOCTYPE html>
<script type="application/javascript" src="panel.js"></script>
<button id="button">Click Me</button>

panel.js:

browser.windows.getCurrent({ populate: true }).then(win => {
  const portName = `port for window ${win.id}`;
  const activeTab = win.tabs.find(tab => tab.active);
  const port = browser.runtime.connect({
    name: portName
  });
  port.postMessage({ tabId: activeTab.id });

  const button = document.getElementById('button');
  button.addEventListener('click', async event => {
    const tabIdFromBackground = await browser.runtime.sendMessage({
      type: 'get-tabId',
      portName
    });
    button.textContent = tabIdFromBackground;
  });
});

在这个例子中,有一个监听器对应一个连接,它被设计为只响应以相应端口名称发送的消息。

基于端口的消息传递不使用 sendResponse。只需 post 另一个消息到端口。

这是基于端口的消息传递系统的一个非常简化的示例。它不传输错误或异常,没有超时。这个想法是传递一个 id,将 id 的回调保存在映射中,并在响应中使用相同的 id 来调用保存的回调。

不像 browser.runtime.sendMessage 每次都创建一个新端口(如果您发送大量消息,这是一个相对昂贵的操作),我们会重复使用同一个端口。

发件人:

const port = browser.runtime.connect({name: 'foo'});
const portMap = new Map();
let portMessageId = 0;

port.onMessage.addListener(msg => {
  const {id, data} = msg;
  const resolve = portMap.get(id);
  portMap.delete(id);
  resolve(data);
});

function send(data) {
  return new Promise(resolve => {
    const id = ++portMessageId;
    portMap.set(id, resolve);
    port.postMessage({id, data});
  });
}

用法:

(async () => {
  const response = await send({foo: 'whatever'});
  console.log(response);
})();

接收者:

/** @param {chrome.runtime.Port} port */
browser.runtime.onConnect.addListener(port => {
  if (port.name === 'foo') {
    port.onMessage.addListener(msg => {
      const {id, data} = msg;
      port.postMessage({id, data: processMessage(data)});
    });
  }
});