ManifestV3新承诺错误:在收到响应之前,消息端口已关闭。

8
我正在开发一个扩展程序,在其内容脚本和后台服务工作者(清单V3)之间进行了大量的消息传递。我注意到了新的基于Promise的V3 API中,尤其是sendResponse()函数出现了一些问题。
对于期望响应的API调用,一切正常。但是,如果我不需要响应并且不提供回调函数或使用promise的.then()方法(或async/await),则会抛出Promise错误——提示"在接收到响应之前,消息端口已关闭"。
奇怪的是,调用仍然有效,所以我想这个错误更像是一个警告。
代码示例:
在内容脚本中,向后台发送消息: ```html chrome.runtime.sendMessage({ greeting: "hello" }); ``` 在后台中,接收消息并发送回复: ```html chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.greeting === "hello") { // do something sendResponse({ farewell: "goodbye" }); } }); ```
chrome.runtime.sendMessage({ type: 'toggle_setting' })

背景脚本接收消息并执行某些操作,然后不发送响应而退出:
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
  if (message.type === 'toggle-setting') {
    //* do whatever it does
  }
})

上述错误是由背景代码引起的。但如果我在其中添加一行代码,并以无参数的方式调用sendResponse()函数,则不会发生错误。

chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
  sendResponse()
  if (message.type === 'toggle-setting') {
    //* do whatever it does
  }
})

因此,这样可以消除错误信息,但是当没有响应需要或预期时,我并不清楚为什么这是必要的。是否有其他方法向基于 Promise 的 V3 API 发出信号,或者即使不需要,现在也需要调用 sendResponse()?

2个回答

12

这是Chrome 99-101中的漏洞,已在Chrome 102中修复。

原因是sendMessage现在在内部被转成Promise,所以我们可以使用await等待它,但副作用是当我们没有自己指定回调函数时,为了使调用返回Promise,它会在内部添加回调函数,这意味着由于我们没有在onMessage中调用sendResponse,API会认为是我们错误地使用回调函数而未提供响应,并报告此错误

解决方法1:在chrome.runtime.onMessage中调用sendResponse()

  • 发送方(问题中是内容脚本):

chrome.runtime.sendMessage('test');
  • 接收器(在问题中是指后台脚本):

  • chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
      doSomethingWithoutResponding(msg);
      sendResponse();
    });
    

    解决方法2:禁止显示这个特定的错误信息

    还可以修补 API 在调用之前缺少调用堆栈的固有问题。

    // sender (in the question it's the content script)
    sendMessage('foo');
    
    // sendMessage('foo').then(res => whatever(res));
    // await sendMessage('foo');
    
    function sendMessage(msg) {
      const err1 = new Error('Callstack before sendMessage:');
      return new Promise((resolve, reject) => {
        chrome.runtime.sendMessage(msg, res => {
          let err2 = chrome.runtime.lastError;
          if (!err2 || err2.message.startsWith('The message port closed before')) {
            resolve(res);
          } else {
            err2 = new Error(err2.message);
            err2.stack += err1.stack.replace(/^Error:\s*/, '');
            reject(err2);
          }
        });
      });
    }
    

    这个问题也适用于 onMessageExternal 吗? - Alex Rothberg

    -1
    在您的消息监听器末尾添加一个return true,以允许异步响应。

    1
    但是 OP 不需要异步响应,而 return true 只会让发送者等待永远不会到来的响应。 - Teepeemm

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接