Chrome 扩展计时器并行实例和端口连接问题

Chrome extension timer parallel instances and port connection issues

我正在尝试 运行 在 chrome 扩展程序的后台运行计时器脚本。在用户从 popup.js.

启动定时器后,我希望定时器成为 运行ning

我做了什么: 我在 popup.jsbackground.js 之间建立了一个端口连接并且成功了。它成功 logs 我的 popup.js 控制台时间。

(问题总结如下)


Popup.js:

//create a port
let port = chrome.runtime.connect({name:"timeScript"});

//in this certain event send duration to background and receive the constantly updating time
play.addEventListener("click", () => {

    port.postMessage({duration: 6*60});

    port.onMessage.addListener((x) => {
        console.log(`${x.time[0]} : ${x.time[1]}`);
    });
});

Background.js:

chrome.runtime.onConnect.addListener((port) => {

    console.assert(port.name = "timeScript");
    
    //listen for messages from popup.js
    port.onMessage.addListener((x) => {
        let duration = x.duration;

        //timer logic
        function startTimer(duration) {
            var start = Date.now(), diff, minutes, seconds;
    
            function timer() {
                diff = duration - (((Date.now() - start) / 1000) | 0);
        
                minutes = (diff / 60) | 0;
                seconds = (diff % 60) | 0;
        
                minutes = minutes < 10 ? "0" + minutes : minutes;
                seconds = seconds < 10 ? "0" + seconds : seconds;
        
                if (diff <= 0) {
                    start = Date.now() + 1000;
                }
                
                //Send message to the popup.js
                port.postMessage({time: [`${minutes}`, `${seconds}`]});

            };
            timer();
            setInterval(timer, 1000);
        }
        //timer logic end

        startTimer(duration)    
    })
})

问题:

  1. 当我 play 我的 popup.js 的计时器在我已经 played 它一次之后,第二个计时器开始 运行ning 并行到第一个。如果我第三次按下它,第三个计时器将 运行 并行,依此类推。

  2. 我知道当弹出 window 关闭时,popup.js 停止 运行ning 因此,端口连接关闭并抛出错误:Uncaught Error: Attempting to use a disconnected port objectbackground.js 中。我希望计时器保持 运行ning 并在再次启动时重新连接到 popup.js。我对此进行了研究,但到目前为止一无所获。

我认为这些问题是由于 'async chrome API functions'(因为我在某处读到类似的东西)造成的,但我无法指出问题所在。


这是我的manifest.json,以备不时之需。

清单:

{
  "name": "Timer",
  "description": "Timer",
  "version": "0.0.0.1",
  "manifest_version": 2,
  "icons": {
    "128": "img/icon_128.png",
    "48": "img/icon_48.png",
    "16": "img/icon_16.png"
  },
  "browser_action": {
      "default_icon": "img/icon_16.png",
      "default_popup": "popup.html"
  },
  "background": {
    "page": "background.html",
    "persistent": true
  }
}

与“异步”无关。问题是您的计时器正在使用对 port 的引用,但弹出窗口仅在显示时运行,因此它在关闭时终止连接,使 port 无法使用(这是您看到的错误)。当弹出窗口再次打开时,您的 onConnect 侦听器将创建一个新的 port 除了旧计时器使用的旧的。

解决办法是将定时器提取到全局范围内,使port全局化:

let popupPort;

chrome.runtime.onConnect.addListener((port) => {
  popupPort = port;
  port.onMessage.addListener((x) => {
    startTimer(x.duration);
  });
  port.onDisconnect.addListener(() => {
    popupPort = null;
  });
});

function startTimer(duration) {
  // ...............
  function timer() {
    // ...............
    if (popupPort) {
      popupPort.postMessage({time: [`${minutes}`, `${seconds}`]});
    }
  }
  // ...............
}

这是一个简化的示例,如果在两个不同的 windows 中同时打开两个弹出窗口,它将无法正常工作。要使其适用于任意数量的弹出窗口,您需要将 popupPort 转换为数组或 Set,正确更新它,并在每个条目上调用 postMessage。