如何与 SharedWorker 共享数据

Posted

技术标签:

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

互联网上有很多关于 SharedWorker 和 *** 的讨论和教程,但没有一个真正实现了最基本的目标——在两个共享 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,但是你需要多个脚本。在您的示例中,您使用相同的脚本/文件/代码初始化它们。所以应该只有一个实例。 【参考方案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);

【讨论】:

好的。现在我有一个演示工作。你的演示有效吗?你介意我用我的代码替换你的代码吗?我想奖励你积分! 我可以添加是的。我还做了一个理论演示应用程序;)

以上是关于如何与 SharedWorker 共享数据的主要内容,如果未能解决你的问题,请参考以下文章

数据共享 如何与读者帐户共享 UDF?

如何与多个用户共享 access 2016 数据库

Ray 究竟是如何与工人共享数据的?

如何共享数据?- 每天5分钟玩转 Docker 容器技术(41)

如何共享数据?- 每天5分钟玩转 Docker 容器技术(41)

delphi 中如何实现对象之间的数据共享