Web Worker 的作用与场景

Web Worker 实现多线程及其使用场景

问题

Web Worker 是什么?有哪些使用场景?如何使用?

解答

Web Worker 让 JavaScript 可以在后台线程运行脚本,不阻塞主线程,避免页面卡顿。

基本用法

主线程 (main.js)

// 创建 Worker
const worker = new Worker('worker.js');

// 发送消息给 Worker
worker.postMessage({ type: 'calculate', data: [1, 2, 3, 4, 5] });

// 接收 Worker 返回的消息
worker.onmessage = (event) => {
  console.log('计算结果:', event.data);
};

// 错误处理
worker.onerror = (error) => {
  console.error('Worker 错误:', error.message);
};

// 终止 Worker
// worker.terminate();

Worker 线程 (worker.js)

// 接收主线程消息
self.onmessage = (event) => {
  const { type, data } = event.data;
  
  if (type === 'calculate') {
    // 执行耗时计算
    const result = data.reduce((sum, num) => sum + num, 0);
    
    // 返回结果给主线程
    self.postMessage(result);
  }
};

实际场景:大数据处理

// worker.js - 处理大量数据排序
self.onmessage = (event) => {
  const data = event.data;
  
  // 耗时的排序操作
  const sorted = data.sort((a, b) => a - b);
  
  self.postMessage(sorted);
};

// main.js
const worker = new Worker('worker.js');
const largeArray = Array.from({ length: 1000000 }, () => Math.random());

worker.postMessage(largeArray);
worker.onmessage = (e) => {
  console.log('排序完成', e.data.slice(0, 10));
};

实际场景:图片处理

// worker.js - 图片灰度处理
self.onmessage = (event) => {
  const imageData = event.data;
  const data = imageData.data;
  
  // 遍历每个像素,转为灰度
  for (let i = 0; i < data.length; i += 4) {
    const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
    data[i] = gray;     // R
    data[i + 1] = gray; // G
    data[i + 2] = gray; // B
  }
  
  self.postMessage(imageData);
};

使用 Blob 创建内联 Worker

// 不需要单独的 worker.js 文件
const workerCode = `
  self.onmessage = (e) => {
    const result = e.data * 2;
    self.postMessage(result);
  };
`;

const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));

worker.postMessage(10);
worker.onmessage = (e) => console.log(e.data); // 20

SharedWorker:多页面共享

// shared-worker.js
const connections = [];

self.onconnect = (event) => {
  const port = event.ports[0];
  connections.push(port);
  
  port.onmessage = (e) => {
    // 广播给所有连接的页面
    connections.forEach(p => p.postMessage(e.data));
  };
};

// 页面中使用
const shared = new SharedWorker('shared-worker.js');
shared.port.start();
shared.port.postMessage('hello');
shared.port.onmessage = (e) => console.log(e.data);

Worker 的限制

// Worker 中不能访问
// - DOM (document, window)
// - parent 对象

// Worker 中可以使用
// - navigator, location (部分)
// - setTimeout, setInterval
// - fetch, XMLHttpRequest
// - IndexedDB
// - importScripts() 加载脚本

// worker.js
importScripts('utils.js', 'helper.js');

关键点

  • 不阻塞主线程:耗时任务放 Worker,保持 UI 流畅
  • 通信靠消息:postMessage 发送,onmessage 接收,数据是拷贝的
  • 无法访问 DOM:Worker 运行在独立上下文,不能操作页面
  • 适用场景:大数据处理、复杂计算、图片/音视频处理、加密解密
  • SharedWorker:可被多个页面共享,适合跨标签页通信