Web Worker 与事件循环

深入理解 Web Worker 的工作机制、与主线程的通信方式、以及如何在实际项目中使用 Worker 处理耗时任务。


Worker 的基本概念

Web Worker 是浏览器提供的在后台线程执行脚本的能力。每个 Worker 有自己的:

  • 事件循环:独立于主线程
  • 全局作用域:不是 window,是 DedicatedWorkerGlobalScope
  • 消息队列:接收来自主线程的消息

主线程和 Worker 通过 postMessage 异步通信。


Worker 的创建和通信

// 主线程
const worker = new Worker('compute.js');

worker.postMessage({ type: 'start', data: 1000000 });

worker.onmessage = function(e) {
  console.log('Worker result:', e.data);
};

worker.onerror = function(err) {
  console.error('Worker error:', err);
};

// compute.js (Worker 脚本)
self.onmessage = function(e) {
  if (e.data.type === 'start') {
    const result = heavyComputation(e.data.data);
    self.postMessage(result);
  }
};

function heavyComputation(n) {
  // 耗时计算
  return n * 2;
}

数据的传递方式

复制传递(Structured Clone)

默认情况下,postMessage 的数据会被复制到 Worker:

// 主线程
const data = { large: new Array(1e6) };
worker.postMessage(data); // 数据被复制

Transferable 对象(转移)

大数据可以用转移模式,避免复制开销:

const buffer = new ArrayBuffer(8 * 1024 * 1024);
worker.postMessage(buffer, [buffer]); // buffer 被转移,主线程无法再访问

Worker 的事件循环

Worker 有自己独立的事件循环,不阻塞主线程:

// Worker 中也可以使用 setTimeout, setInterval
self.setInterval(() => {
  self.postMessage('heartbeat');
}, 1000);

// Worker 中也可以使用 Promise
Promise.resolve().then(() => console.log('microtask in worker'));

常见应用场景

图像处理

// Worker 处理图片滤镜
self.onmessage = function(e) {
  const imageData = e.data;
  const data = imageData.data;

  for (let i = 0; i < data.length; i += 4) {
    // 灰度滤镜
    const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    data[i] = data[i + 1] = data[i + 2] = avg;
  }

  self.postMessage(imageData);
};

加密解密

// Worker 处理加密
async function encryptWithWorker(data, key) {
  const worker = new Worker('crypto.js');

  return new Promise((resolve, reject) => {
    worker.onmessage = (e) => resolve(e.data);
    worker.onerror = (e) => reject(e.error);
    worker.postMessage({ data, key });
  });
}

Worker 的生命周期

  1. 创建:通过 new Worker() 创建
  2. 运行:Worker 脚本开始执行
  3. 终止:通过 worker.terminate() 或 Worker 内部 self.close() 终止
// 主动终止 Worker
worker.terminate();

这一章想说的

Web Worker 提供了在后台线程执行 JavaScript 的能力,实现了真正的多线程并行。

关键点:

  • Worker 有独立的事件循环,不阻塞主线程
  • 主线程和 Worker 通过 postMessage 异步通信
  • 数据默认是复制传递,大数据可用 Transferable 转移

Worker 是处理 CPU 密集型任务的正确方式,而不是用 setTimeout 分割任务。


延展阅读