二、专用工作者线程

114 阅读1分钟

(一)、 基础版的创建、通信和关闭(只能加载同源的脚本来创建)

  1. 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>
  1. 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);
});
  1. 关闭(关闭后无法通过实例重启,只能通过脚本再次创建新的实例)
// index.html
worker.terminate();

// worker.js
self.close();

(二)、其他的创建方式

  1. 在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])));
  1. 在工作者线程中动态执行脚本
  • 在工作者线程内部可以请求来自任何源的脚本, 导入策略类似于生成的
// worker.js
const scriptUrl = 'xxx.com/xxx.js'
importScripts(scriptUrl);

(三)、 通信方式

  1. postMessage
// index.html
worker.postMessage()
woker.addEventListener(‘message’, (ev) ⇒ {console.log(ev)});
// worker-01.js
self.postMessage()
self.addEventListener(‘message’, (ev) ⇒ {console.log(ev)})
  1. 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('这是发送的消息');
        };
    }
};
  1. 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');
}

(四)、数据传输

  1. 结构化克隆算法(structured clone algorithm)

  2. 可转移对象(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
  })
  1. 共享数组缓冲区(shared array buffers)
    • 在工作者线程中,通过Atomics对象程获得SharedArrayBuffer实例的锁,再执行完全部读/写/读操作来开发多线程的同步问题。