(一)、 基础版的创建、通信和关闭(只能加载同源的脚本来创建)
- index.html 中的主线程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>main</title>
</head>
<body>
<script>
// 1. 创建 webworker 获取worker实例
const worker = new Worker('./worker.js');
// 2. 发送消息给worker实例
worker.postMessage('来自主线程的消息');
// 3. 监听worker实例发送的消息
worker.addEventListener('message', (message) => console.log('接到worker发来的消息: ',message));
// 工作者线程沙盒会阻止它打断父线程的执行,但可以在worker设置监听访问到
worker.addEventListener('error', (error) => console.log(error));
worker.addEventListener('messageerror', (messageerror) => console.log(messageerror));
</script>
</body>
</html>
- worker.js 中的worker线程
console.log('welcome to worker!!!');
// this和self都能用,但是推荐用self
console.log(this);
// 设置当前worker的名字
self.name = '1号打工人';
console.log(self);
setTimeout(function () {
self.postMessage('来自' + self.name + '的消息');
}, 2000);
self.addEventListener('message', function(ev) {
console.log('接到主线程的消息: ', ev, ev.data);
});
- 关闭(关闭后无法通过实例重启,只能通过脚本再次创建新的实例)
// index.html
worker.terminate();
// worker.js
self.close();
(二)、其他的创建方式
- 在JavaScript行内创建工作者线程
// index.html
function fibonacci(n) {
return n < 1 ? 0
: n <= 2 ? 1
: fibonacci(n -1) + fibonacci(n - 2);
}
const workerScript = `
self.name = '3 号打工人';
self.postMessage(
${fibonacci.toString()}(9)
)`;
const worker = new Worker(URL.createObjectURL(new Blob([workerScript])));
- 在工作者线程中动态执行脚本
- 在工作者线程内部可以请求来自任何源的脚本, 导入策略类似于生成的
// worker.js
const scriptUrl = 'xxx.com/xxx.js'
importScripts(scriptUrl);
(三)、 通信方式
- postMessage
// index.html
worker.postMessage()
woker.addEventListener(‘message’, (ev) ⇒ {console.log(ev)});
// worker-01.js
self.postMessage()
self.addEventListener(‘message’, (ev) ⇒ {console.log(ev)})
- MessageChannel
// index.html
const channel = new MessageChannel();
const worker = new Worker('./worker.js');
// 工作者线程负责初始化信息通道
worker.postMessage(null, [channel.port1]);
// 通过信息通道发送数据
channel.port2.addEventListener('message', ({data}) => {console.log(data);});
// 通过信息通道发送数据
channel.port2.postMessage('hello');
// worker.js
// 存储messagePort
let messagePort = null;
// 消息处理的监听
self.onmessage = ({ports}) => {
if(!messagePort) {
messagePort = ports[0];
self.onmessage = null;
messagePort.onmessage = ({data}) => {
console.log('接受到了消息');
messagePort.postMessage('这是发送的消息');
};
}
};
- BroadCastChannel
发布订阅的模式
// index.html
const channel = new BroaderChannel('worker_channel');
const worker = new Worker('./worker.js');
channel.onmessage = ({data}) => {
console.log(`heard ${data} on page`);
}
setTimeout(() => channel.postMessage('foo'), 1000);
// worker.js
const channel = new BroaderChannel('worker_channel');
channel.onmessage = ({data}) => {
console.log(`heard ${data} on page`);
channel.postMessage('bar');
}
(四)、数据传输
-
结构化克隆算法(structured clone algorithm)
-
可转移对象(transferable objects)
// index.html
const worker = new Worker('./worker.js');
const arrayBuffer = new ArrayBuffer(32);
console.log(`page's buffer size: ${arrayBuffer.length}`); // 32
worker.postMessage({foo: {bar: arrayBuffer}}, [arrayBuffer]);
console.log(`page's buffer size: ${arrayBuffer.length}`); // 0
// worker.js
self.addEventListener('message', (message) => {
console.log(`worker's buffer size: ${arrayBuffer.length}`) // 32
})
- 共享数组缓冲区(shared array buffers)
- 在工作者线程中,通过Atomics对象程获得SharedArrayBuffer实例的锁,再执行完全部读/写/读操作来开发多线程的同步问题。