Web Workers

Web Workers 提供了在后台线程运行 JavaScript 代码的能力,避免阻塞主线程,提升应用性能和响应速度。

参考规范Web Workers


📚 目录


1. Web Workers 概述

1.1 什么是 Web Workers

Web Workers 允许在后台线程运行 JavaScript,不会阻塞主线程。

特点

  • 在独立线程中运行
  • 不能直接访问 DOM
  • 通过消息与主线程通信
  • 适合 CPU 密集型任务

1.2 浏览器支持

// 检查支持
if (typeof Worker !== 'undefined') {
  // 支持 Web Workers
} else {
  console.warn('Web Workers not supported');
}

2. 基本用法

2.1 创建 Worker

// 创建 Worker(需要单独的 JS 文件)
const worker = new Worker('worker.js');
 
// 或者使用 Blob URL
const workerCode = `
  self.onmessage = function(e) {
    console.log('Worker received:', e.data);
    self.postMessage('Hello from worker');
  };
`;
 
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));

2.2 Worker 文件

// worker.js
// 接收主线程消息
self.onmessage = function(e) {
  const data = e.data;
  console.log('Worker received:', data);
  
  // 处理数据
  const result = processData(data);
  
  // 发送结果回主线程
  self.postMessage(result);
};
 
// 处理数据
function processData(data) {
  // 执行耗时操作
  let sum = 0;
  for (let i = 0; i < data.length; i++) {
    sum += data[i];
  }
  return sum;
}

2.3 主线程通信

const worker = new Worker('worker.js');
 
// 发送消息到 Worker
worker.postMessage({ type: 'calculate', data: [1, 2, 3, 4, 5] });
 
// 接收 Worker 消息
worker.onmessage = function(e) {
  console.log('Main thread received:', e.data);
};
 
// 错误处理
worker.onerror = function(error) {
  console.error('Worker error:', error);
};
 
// 终止 Worker
worker.terminate();

3. 消息传递

3.1 基本消息

// 主线程
const worker = new Worker('worker.js');
 
// 发送简单数据
worker.postMessage('Hello Worker');
 
// 发送对象
worker.postMessage({
  command: 'process',
  data: [1, 2, 3, 4, 5]
});
 
// Worker
self.onmessage = function(e) {
  const message = e.data;
  if (typeof message === 'string') {
    console.log('String:', message);
  } else if (message.command === 'process') {
    processData(message.data);
  }
};

3.2 传递复杂数据

// 传递 ArrayBuffer(零拷贝)
const buffer = new ArrayBuffer(1024);
worker.postMessage(buffer, [buffer]);  // 转移所有权
 
// 传递 TypedArray
const array = new Uint8Array([1, 2, 3, 4, 5]);
worker.postMessage(array.buffer, [array.buffer]);
 
// Worker 接收
self.onmessage = function(e) {
  const buffer = e.data;
  const view = new Uint8Array(buffer);
  console.log('Received buffer:', view);
};

3.3 消息通道

// 创建消息通道
const channel = new MessageChannel();
 
// 主线程
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');
 
// 将端口传递给 Worker
worker1.postMessage({ port: channel.port1 }, [channel.port1]);
worker2.postMessage({ port: channel.port2 }, [channel.port2]);
 
// Worker 之间可以直接通信
// worker1.js
self.onmessage = function(e) {
  const port = e.data.port;
  port.onmessage = function(event) {
    console.log('Worker1 received:', event.data);
  };
  port.postMessage('Hello from Worker1');
};

4. 错误处理

4.1 Worker 错误

// 主线程错误处理
const worker = new Worker('worker.js');
 
worker.onerror = function(error) {
  console.error('Worker error:', error.message);
  console.error('Filename:', error.filename);
  console.error('Line number:', error.lineno);
};
 
// Worker 内部错误处理
// worker.js
try {
  // 可能出错的代码
  processData();
} catch (error) {
  self.postMessage({
    type: 'error',
    message: error.message
  });
}

4.2 错误传播

// Worker 发送错误到主线程
// worker.js
self.onmessage = function(e) {
  try {
    const result = riskyOperation(e.data);
    self.postMessage({ type: 'success', data: result });
  } catch (error) {
    self.postMessage({
      type: 'error',
      message: error.message,
      stack: error.stack
    });
  }
};
 
// 主线程处理
worker.onmessage = function(e) {
  if (e.data.type === 'error') {
    console.error('Worker error:', e.data.message);
  } else {
    console.log('Result:', e.data.data);
  }
};

5. 共享 Worker

5.1 创建共享 Worker

// 创建共享 Worker(多个页面可以共享)
const sharedWorker = new SharedWorker('shared-worker.js');
 
// 通过 port 通信
sharedWorker.port.onmessage = function(e) {
  console.log('Received:', e.data);
};
 
sharedWorker.port.postMessage('Hello Shared Worker');
 
// 启动端口
sharedWorker.port.start();
 
// shared-worker.js
let connections = [];
 
self.onconnect = function(e) {
  const port = e.ports[0];
  connections.push(port);
  
  port.onmessage = function(event) {
    // 广播到所有连接
    connections.forEach(conn => {
      if (conn !== port) {
        conn.postMessage(event.data);
      }
    });
  };
  
  port.start();
};

6. 实际应用

6.1 图像处理

// 主线程
const worker = new Worker('image-processor.js');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
 
// 发送图像数据到 Worker
worker.postMessage(imageData);
 
worker.onmessage = function(e) {
  // 接收处理后的图像
  ctx.putImageData(e.data, 0, 0);
};
 
// image-processor.js
self.onmessage = function(e) {
  const imageData = e.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);
};

6.2 大数据计算

// 主线程
const worker = new Worker('calculator.js');
 
function calculateLargeSum() {
  const data = new Array(10000000).fill(0).map((_, i) => i);
  
  worker.postMessage({
    type: 'sum',
    data: data
  });
  
  worker.onmessage = function(e) {
    console.log('Sum:', e.data);
  };
}
 
// calculator.js
self.onmessage = function(e) {
  if (e.data.type === 'sum') {
    const sum = e.data.data.reduce((a, b) => a + b, 0);
    self.postMessage(sum);
  }
};

6.3 Worker 池

class WorkerPool {
  constructor(workerScript, poolSize = 4) {
    this.workerScript = workerScript;
    this.poolSize = poolSize;
    this.workers = [];
    this.queue = [];
    this.activeWorkers = 0;
    
    this.init();
  }
  
  init() {
    for (let i = 0; i < this.poolSize; i++) {
      const worker = new Worker(this.workerScript);
      worker.idle = true;
      this.workers.push(worker);
    }
  }
  
  execute(data) {
    return new Promise((resolve, reject) => {
      this.queue.push({ data, resolve, reject });
      this.processQueue();
    });
  }
  
  processQueue() {
    if (this.queue.length === 0) return;
    if (this.activeWorkers >= this.poolSize) return;
    
    const worker = this.workers.find(w => w.idle);
    if (!worker) return;
    
    const task = this.queue.shift();
    worker.idle = false;
    this.activeWorkers++;
    
    const handleMessage = (e) => {
      worker.removeEventListener('message', handleMessage);
      worker.removeEventListener('error', handleError);
      worker.idle = true;
      this.activeWorkers--;
      task.resolve(e.data);
      this.processQueue();
    };
    
    const handleError = (error) => {
      worker.removeEventListener('message', handleMessage);
      worker.removeEventListener('error', handleError);
      worker.idle = true;
      this.activeWorkers--;
      task.reject(error);
      this.processQueue();
    };
    
    worker.addEventListener('message', handleMessage);
    worker.addEventListener('error', handleError);
    worker.postMessage(task.data);
  }
  
  terminate() {
    this.workers.forEach(worker => worker.terminate());
  }
}
 
// 使用
const pool = new WorkerPool('worker.js', 4);
 
async function processMultipleTasks() {
  const tasks = [1, 2, 3, 4, 5, 6, 7, 8];
  const results = await Promise.all(
    tasks.map(data => pool.execute(data))
  );
  console.log('Results:', results);
}

6.4 定时任务

// 在 Worker 中执行定时任务
// worker.js
let intervalId;
 
self.onmessage = function(e) {
  if (e.data.command === 'start') {
    intervalId = setInterval(() => {
      const data = performTask();
      self.postMessage({ type: 'tick', data });
    }, e.data.interval);
  } else if (e.data.command === 'stop') {
    clearInterval(intervalId);
  }
};
 
function performTask() {
  // 执行任务
  return { timestamp: Date.now() };
}
 
// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ command: 'start', interval: 1000 });
 
worker.onmessage = function(e) {
  if (e.data.type === 'tick') {
    console.log('Tick:', e.data.data);
  }
};

📖 参考资源


javascript web-workers 多线程 性能优化 前端基础