如何与 SharedWorker 共享数据
How to share data with SharedWorker
网上和Whosebug上已经有很多关于SharedWorker的讨论和教程,但是none确实实现了最基本的目标——在两个shared worker之间直接传递数据。
对我来说,SharedWorker 优于专用网络 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();
}
点击 'Initiate A' 和 'Initiate B' 后,我在控制台上收到以下错误消息:
shared.html:15 未捕获的 DOMException:无法在 'MessagePort' 上执行 'postMessage':无法克隆 MessagePort,因为它未传输。
换句话说,我无法通过端口。
SharedWorker 也有任何用处。看来大多数情况下普通的dedicated Worker就够用了
所以基本上你在使用 SharedWorkers 是错误的。
来自文档:https://developer.mozilla.org/de/docs/Web/API/SharedWorker
The SharedWorker interface represents a specific kind of worker that can be accessed from several browsing contexts, such as several windows, iframes or even workers.
这意味着您可以跨多个 windows/选项卡/浏览上下文
交流和计算内容
|-----------| |-----------|
| 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.
}
您可以向 worker 发送附加端口,但 MessagePort 是可传输对象。这些需要在发送时添加到传输列表中。
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);
}
网上和Whosebug上已经有很多关于SharedWorker的讨论和教程,但是none确实实现了最基本的目标——在两个shared worker之间直接传递数据。
对我来说,SharedWorker 优于专用网络 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();
}
点击 'Initiate A' 和 'Initiate B' 后,我在控制台上收到以下错误消息:
shared.html:15 未捕获的 DOMException:无法在 'MessagePort' 上执行 'postMessage':无法克隆 MessagePort,因为它未传输。
换句话说,我无法通过端口。
SharedWorker 也有任何用处。看来大多数情况下普通的dedicated Worker就够用了
所以基本上你在使用 SharedWorkers 是错误的。
来自文档:https://developer.mozilla.org/de/docs/Web/API/SharedWorker
The SharedWorker interface represents a specific kind of worker that can be accessed from several browsing contexts, such as several windows, iframes or even workers.
这意味着您可以跨多个 windows/选项卡/浏览上下文
交流和计算内容|-----------| |-----------|
| 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.
}
您可以向 worker 发送附加端口,但 MessagePort 是可传输对象。这些需要在发送时添加到传输列表中。
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);
}