【问题标题】:How to share data with SharedWorker如何与 SharedWorker 共享数据
【发布时间】:2020-10-03 07:44:11
【问题描述】:

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

对我来说,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 也是如此。似乎在大多数情况下,普通的敬业工人就足够了。

【问题讨论】:

  • 你到底想做什么?你为什么尝试发送消息端口与工人交谈?但基本上,要发送一个端口,您需要在 transferList b.postMessage({type:'start', port:a}, [a]); 中引用它。
  • 我正在尝试让工人 a 直接与工人 b 交谈。
  • 但是由于这是sharedWorker,所以这些都是一样的。
  • 你是说SharedWorker只能有一个?
  • 不,我是说:如果两个共享工作者被同一个脚本在同一个源上加载,它只会产生一个共享工作者。你可以有多个 Shared Worker,但是你需要多个脚本。在您的示例中,您使用相同的脚本/文件/代码初始化它们。所以应该只有一个实例。

标签: javascript web worker webapi shared-worker


【解决方案1】:

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

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

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

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

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

在上下文中发送启动 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);
}

【讨论】:

  • 好的。现在我有一个演示工作。你的演示有效吗?你介意我用我的代码替换你的代码吗?我想奖励你积分!
  • 我可以添加是的。我还做了一个理论演示应用程序;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 1970-01-01
相关资源
最近更新 更多