如何与 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 也是如此。似乎在大多数情况下,普通的敬业工人就足够了。
【问题讨论】:
你到底想做什么?你为什么尝试发送消息端口与工人交谈?但基本上,要发送一个端口,您需要在 transferListb.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 共享数据的主要内容,如果未能解决你的问题,请参考以下文章
如何共享数据?- 每天5分钟玩转 Docker 容器技术(41)