Cho*_*ang 2 javascript worker web shared-worker webapi
互联网和 StackOverflow 上已经有很多关于 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>
Run Code Online (Sandbox Code Playgroud)
(共享.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();
}
Run Code Online (Sandbox Code Playgroud)
单击“启动 A”然后单击“启动 B”后,我在控制台上收到以下错误消息:
shared.html:15 未捕获的 DOMException:无法在“MessagePort”上执行“postMessage”:无法克隆 MessagePort,因为它没有传输。
换句话说,我无法传递端口。
SharedWorker 根本没有任何用处。似乎在大多数情况下,常规的专用 Worker 就足够了。
所以基本上你使用 SharedWorkers 是错误的。
来自文档:https : //developer.mozilla.org/de/docs/Web/API/SharedWorker
SharedWorker 接口代表可以从多个浏览上下文访问的特定类型的工作器,例如多个窗口、iframe 甚至工作器。
这意味着您可以跨多个窗口/选项卡/浏览上下文进行通信和计算内容
|-----------| |-----------|
| Window 1 | | Window 2 |
| | | |
| | | |
|-----------| |-----------|
| |
__________________
|
|-----------|
| Worker |
| |
|-----------|
Run Code Online (Sandbox Code Playgroud)
在上下文中发送启动工作器将在 SharedWorker 上打开一个端口
|-----------| |-----------|
| Window 1 | | Window 2 |
| | | |
| | | |
|-----------| |-----------|
| |
__________________
|
|-----------|
| Worker |
| |
|-----------|
Run Code Online (Sandbox Code Playgroud)
您可以向工作人员发送其他端口,但 MessagePorts 是 Transferable 对象。这些需要在发送时添加到传输列表中。
//============================================
//== 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.
}Run Code Online (Sandbox Code Playgroud)
如果您只想将数据传递给其他上下文,请使用 BrodcastChannel:
https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API
** 编辑 **
这是一个工作演示。尝试在一个选项卡上打开 shared.html,在另一个选项卡上打开 shared2.html。您将在第二个选项卡上看到,数字不会从 0 开始。
(共享.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>
Run Code Online (Sandbox Code Playgroud)
(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>
Run Code Online (Sandbox Code Playgroud)
(共享.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);
}
Run Code Online (Sandbox Code Playgroud)