2

网上已经有很多关于 SharedWorker 和 StackOverflow 的讨论和教程,但没有一个真正实现了最基本的目标——直接在两个 SharedWorker 之间传递数据。

对我来说,SharedWorker 相对于专用 web Worker 的优势在于前者允许通过不同的选项卡、iframe 和线程进行直接通信。我尝试了以下方法:

(shared.html:)

<!DOCTYPE html><html><head></head><body>
<button onclick="init('a')">Initiate A</button>
<button onclick="init('b')">Initiate B</button>
<script>

var a,b;
function init(v) {
    if (v=='a'){
        a = (new SharedWorker('./shared.js')).port;
        a.start();
        a.postMessage({type:'start', port:b})
    } else {
        b = (new SharedWorker('./shared.js')).port;
        b.start();
        b.postMessage({type:'start', port:a});    // <== error here
    }
}

</script></body></html>

(shared.js)

let peer = null;

onconnect = function (ev) {
    let port = ev.ports[0];
    port.onmessage = (e) => {
        if (e.data.type=='start' && e.data.port){
            peer=e.data.port;
        } else if (e.data.type=='msg' && peer){
            setInterval(()=>{
                peer.postMessage({type:'msg',msg:'greetings!'});
            },2000);
        }
    }
    port.start();
}

单击“启动 A”然后单击“启动 B”后,我在控制台上收到以下错误消息:

shared.html:15 未捕获的 DOMException:无法在“MessagePort”上执行“postMessage”:无法克隆 MessagePort,因为它没有被传输。

换句话说,我无法通过端口。

任何用途的 SharedWorker 也是如此。似乎在大多数情况下,常规的专用 Worker 就足够了。

4

1 回答 1

6

所以基本上你使用 SharedWorkers 是错误的。

来自文档:https ://developer.mozilla.org/de/docs/Web/API/SharedWorker

SharedWorker 接口代表一种特定类型的工作人员,可以从多个浏览上下文访问,例如多个窗口、iframe 甚至工作人员。

这意味着您可以跨多个窗口/选项卡/浏览上下文进行交流和计算

|-----------|    |-----------|
|  Window 1 |    |  Window 2 |
|           |    |           |
|           |    |           |
|-----------|    |-----------|
      |                |
      __________________
              |
        |-----------|
        |   Worker  |
        |           |
        |-----------|

在上下文中发送启动工作程序将在 SharedWorker 上打开一个端口

//============================================
//== Site Script
//============================================

var worker = new SharedWorker('something.js');
worker.port.start(); // this will trigger the on connect event on the webworker
// this will also start the worker IF this is the first call!

// recieve message from worker
worker.port.addEventListener('message', message => {
  console.log(message);
});

// send a mesasge to the worker
worker.port.postMessage(['I have a nice message for all']);



//============================================
//== Shared Worker
//============================================
const allPorts = [];

onconnect = function(e) {
  // the incoming port
  var port = e.ports[0];
  allPorts.push(port);

  port.addEventListener('message', function(e) {
    // get the message sent to the worker
    var message = e.data[0];
    // send the message to ALL connected worker ports!
    allPorts.forEach(port => {
      port.postMessage(message);
    })
  });

  port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.
}

您可以将 Additional Ports 发送给 worker,但 MessagePorts 是 Transferable 对象。这些需要在发送时添加到传输列表中。

const startA = document.getElementById('startA');
const startB = document.getElementById('startB');

const workerScript = 'console.log("started")';
const blob = new Blob([workerScript]);
const workerScriptURL = URL.createObjectURL(blob);

const worker = new Worker(workerScriptURL);
const messageChannel = new MessageChannel();

worker.postMessage({port: messageChannel.port2}, [messageChannel.port2]);
//                                                    ^ This is the transfer list!

如果您只想将数据传递到其他上下文,请使用 BrodcastChannel:

https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

** 编辑 **

这是一个工作演示。尝试在一个选项卡上打开 shared.html,在另一个选项卡上打开 shared2.html。您将在第二个选项卡上看到,数字不会从 0 开始。

(shared.html)

<!DOCTYPE html><html><head></head><body>
<button onclick="init()">Initiate</button>
<script>

function init() {
    w = (new SharedWorker('./shared.js')).port;
    w.start();
    w.postMessage(0);
    w.onmessage=e=>{
        console.log(e.data);
        w.postMessage(e.data[0]+1);
    };
}

</script></body></html>

(shared2.html)

<!DOCTYPE html><html><head></head><body>
<button onclick="init()">Initiate</button>
<script>

function init() {
    w = (new SharedWorker('./shared.js')).port;
    w.start();
    w.onmessage=e=>{
        console.log(e.data);
    };
}

</script></body></html>

(shared.js)

const ports = [];

onconnect = function (ev) {
    let port = ev.ports[0];
    port.onmessage = (e) => {
        setTimeout(()=>{
            ports.forEach(p=>p.postMessage([e.data, ev.ports.length]));
        },300);
    }
    port.start();
    ports.push(port);
}
于 2020-10-03T08:37:58.240 回答