Web Worker 是浏览器提供的多线程解决方案,允许 JavaScript 在主线程之外的后台线程中执行脚本,避免阻塞 UI。
| 属性 | 内容 |
|---|---|
| 引入标准 | HTML5 (2009) |
| 核心目标 | 解决 JavaScript 单线程性能瓶颈 |
| 执行环境 | 完全隔离的全局上下文 |
| DOM 访问 | ❌ 无法直接访问 |
| 通信方式 | postMessage / onmessage |
| 浏览器支持 | 所有现代浏览器 |
JavaScript 单线程模型的问题:
主线程 (Single Thread)
├── 用户交互 (点击、滚动)
├── DOM 渲染
├── JavaScript 执行
└── 长时间计算 → 页面卡顿 ("卡成PPT")
Web Worker 的价值:
最常用的 Worker 类型,一个 Worker 实例只能被一个页面脚本访问。
// main.js - 主线程
const worker = new Worker('worker.js');
worker.postMessage({ task: 'heavy-computation', data: largeArray });
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
// 终止 Worker
worker.terminate();
// worker.js - Worker 线程
self.onmessage = (e) => {
const { task, data } = e.data;
// 执行耗时计算
const result = heavyComputation(data);
// 返回结果
self.postMessage(result);
};
// 或主动关闭
self.close();
多个浏览上下文可以共享同一个 Worker,需要同源策略。
// shared-worker.js
const connections = [];
self.onconnect = (e) => {
const port = e.ports[0];
connections.push(port);
port.onmessage = (event) => {
// 广播给所有连接
connections.forEach(conn => {
if (conn !== port) {
conn.postMessage(event.data);
}
});
};
port.start();
};
特殊的 Worker 类型,主要用于 PWA 的离线缓存、推送通知等。
// service-worker.js
self.addEventListener('install', (e) => {
// 缓存核心资源
});
self.addEventListener('fetch', (e) => {
// 拦截网络请求
});
| 方式 | 复制/共享 | 性能 | 适用场景 |
|---|---|---|---|
| 结构化克隆 | 深拷贝 | 🐌 慢 (100MB ≈ 250-300ms) | 小数据、复杂对象 |
| Transferable Objects | 所有权转移 | ⚡ 快 (近乎0ms) | 大数据、二进制 |
| SharedArrayBuffer | 共享内存 | ⚡⚡ 最快 | 高性能并发 |
默认方式,浏览器使用结构化克隆算法深拷贝数据。
// 主线程
worker.postMessage({
data: largeObject, // 会被完整复制
timestamp: Date.now()
});
特点:
100MB ArrayBuffer:
- 结构化克隆: 250-300ms
- 内存占用: 200MB (两份拷贝)
零拷贝传输,将数据所有权从主线程转移到 Worker。
// 主线程
const buffer = new ArrayBuffer(100 * 1024 * 1024); // 100MB
// 转移所有权 (而非复制)
worker.postMessage(
{ buffer: buffer }, // 数据
[buffer] // 转移列表
);
// 转移后,主线程的 buffer 变为不可用
console.log(buffer.byteLength); // 0
// Worker 线程
self.onmessage = (e) => {
const buffer = e.data.buffer;
// Worker 现在拥有这份内存
// 处理完成后,可以转移回主线程
self.postMessage({ result: buffer }, [buffer]);
};
支持的 Transferable 类型:
ArrayBufferMessagePortImageBitmapOffscreenCanvas// 音频处理场景 - 避免拷贝音频数据
const audioBuffer = new ArrayBuffer(audioData.length);
// 转移到 Worker 进行 AI 推理
worker.postMessage(
{
audio: audioBuffer,
sampleRate: 16000
},
[audioBuffer] // 零拷贝传输
);
真正的共享内存,多线程可以同时读写同一块内存。
// 主线程
const sharedBuffer = new SharedArrayBuffer(1024 * 1024); // 1MB
const view = new Int32Array(sharedBuffer);
// 创建多个 Worker
const worker1 = new Worker('worker.js');
const worker2 = new Worker('worker.js');
worker1.postMessage({ buffer: sharedBuffer, id: 1 });
worker2.postMessage({ buffer: sharedBuffer, id: 2 });
// worker.js
self.onmessage = (e) => {
const { buffer, id } = e.data;
const view = new Int32Array(buffer);
// 原子操作确保并发安全
Atomics.add(view, 0, 1); // 索引0的值加1
};
安全要求 (COOP/COEP):
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
问题: 频繁创建/销毁 Worker 开销大
解决方案: 预创建 Worker 池,复用实例
// worker-pool.js
class WorkerPool {
constructor(workerScript, poolSize = 4) {
this.workerScript = workerScript;
this.poolSize = Math.min(poolSize, navigator.hardwareConcurrency || 4);
this.workers = [];
this.queue = [];
this.taskId = 0;
// 初始化 Worker 池
for (let i = 0; i < this.poolSize; i++) {
this.addWorker();
}
}
addWorker() {
const worker = new Worker(this.workerScript);
worker.isBusy = false;
worker.currentTask = null;
worker.onmessage = (e) => {
if (worker.currentTask) {
worker.currentTask.resolve(e.data);
worker.currentTask = null;
}
worker.isBusy = false;
this.processQueue();
};
worker.onerror = (error) => {
if (worker.currentTask) {
worker.currentTask.reject(error);
worker.currentTask = null;
}
worker.isBusy = false;
};
this.workers.push(worker);
}
async execute(data, transferList = []) {
return new Promise((resolve, reject) => {
const task = {
id: ++this.taskId,
data,
transferList,
resolve,
reject
};
this.queue.push(task);
this.processQueue();
});
}
processQueue() {
if (this.queue.length === 0) return;
const availableWorker = this.workers.find(w => !w.isBusy);
if (!availableWorker) return;
const task = this.queue.shift();
availableWorker.isBusy = true;
availableWorker.currentTask = task;
availableWorker.postMessage(task.data, task.transferList);
}
terminate() {
this.workers.forEach(w => w.terminate());
this.workers = [];
}
}
// 使用
const pool = new WorkerPool('ai-worker.js', 4);
const result = await pool.execute({ text: 'Hello' });
// ai-worker.js
import { pipeline } from '@huggingface/transformers';
let classifier = null;
let isLoading = false;
// 初始化模型
async function initModel() {
if (classifier || isLoading) return;
isLoading = true;
try {
classifier = await pipeline(
'sentiment-analysis',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{
quantized: true, // 使用量化模型
revision: 'main'
}
);
self.postMessage({ type: 'ready' });
} catch (error) {
self.postMessage({ type: 'error', error: error.message });
} finally {
isLoading = false;
}
}
// 执行推理
async function runInference(text) {
if (!classifier) {
await initModel();
}
const result = await classifier(text);
return result;
}
self.onmessage = async (e) => {
const { id, text, type } = e.data;
try {
if (type === 'init') {
await initModel();
return;
}
const result = await runInference(text);
// 返回结果
self.postMessage({
id,
type: 'result',
result
});
} catch (error) {
self.postMessage({
id,
type: 'error',
error: error.message
});
}
};
// 立即开始加载模型
initModel();
// main.js - 主线程
class TransformersWorker {
constructor() {
this.worker = new Worker('ai-worker.js', { type: 'module' });
this.pendingTasks = new Map();
this.taskId = 0;
this.worker.onmessage = (e) => {
const { id, type, result, error } = e.data;
if (type === 'ready') {
console.log('AI Model loaded');
return;
}
const task = this.pendingTasks.get(id);
if (!task) return;
if (type === 'result') {
task.resolve(result);
} else {
task.reject(new Error(error));
}
this.pendingTasks.delete(id);
};
}
async classify(text) {
return new Promise((resolve, reject) => {
const id = ++this.taskId;
this.pendingTasks.set(id, { resolve, reject });
this.worker.postMessage({ id, text });
});
}
terminate() {
this.worker.terminate();
}
}
// 使用
const ai = new TransformersWorker();
const result = await ai.classify('I love this product!');
console.log(result); // [{label: 'POSITIVE', score: 0.999}]
// worker.js
self.onerror = (error) => {
console.error('Worker error:', error.message);
self.postMessage({
type: 'error',
message: error.message,
stack: error.stack
});
};
self.onmessageerror = (error) => {
console.error('Message error:', error);
};
// 主线程
worker.onerror = (error) => {
console.error('Worker failed:', error.message);
// 可尝试重启 Worker
};
worker.onmessageerror = (error) => {
console.error('Message deserialization failed:', error);
};
// worker.js - 支持进度反馈
self.onmessage = async (e) => {
const { id, data, signal } = e.data;
try {
const result = await processLargeData(data, {
onProgress: (percent) => {
self.postMessage({ id, type: 'progress', percent });
},
signal // AbortSignal 支持
});
self.postMessage({ id, type: 'complete', result });
} catch (error) {
if (error.name === 'AbortError') {
self.postMessage({ id, type: 'cancelled' });
} else {
self.postMessage({ id, type: 'error', error: error.message });
}
}
};
// 主线程
const controller = new AbortController();
worker.postMessage({
id: 1,
data: largeData,
signal: controller.signal // 需要通过 transfer 传递
});
// 取消任务
setTimeout(() => controller.abort(), 5000);
// 根据 CPU 核心数决定 Worker 数量
const optimalWorkerCount = navigator.hardwareConcurrency
? navigator.hardwareConcurrency - 1 // 保留一个核心给主线程
: 3;
console.log(`Optimal workers: ${optimalWorkerCount}`);
浏览器限制:
// 及时释放 Worker
worker.terminate();
// 在 Worker 中手动清理
self.onmessage = (e) => {
const result = process(e.data);
// 主动清理大对象
e.data = null;
self.postMessage(result);
};
// 分块处理,避免一次性加载
const chunkSize = 1000;
async function processInChunks(data) {
const results = [];
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
const result = await workerPool.execute(chunk);
results.push(...result);
// 每处理完一块,给主线程一个喘息机会
await new Promise(resolve => setTimeout(resolve, 0));
}
return results;
}
| 场景 | 是否推荐 | 说明 |
|---|---|---|
| AI 模型推理 | ✅ 强烈推荐 | Transformers.js、ONNX Runtime |
| 大数据排序 | ✅ 推荐 | Excel 表格、数据分析 |
| 图像处理 | ✅ 推荐 | 滤镜、压缩、Canvas 操作 |
| 音频/视频编解码 | ✅ 推荐 | 实时处理 |
| 简单 UI 交互 | ❌ 不推荐 | 开销大于收益 |
| < 100ms 任务 | ❌ 不推荐 | 通信开销不划算 |
| 需要 DOM 操作 | ❌ 不支持 | Worker 无法访问 DOM |
数据大小?
├── < 1KB → 结构化克隆 (默认)
├── 1KB ~ 10MB → Transferable Objects (ArrayBuffer)
└── > 10MB + 频繁访问 → SharedArrayBuffer + Atomics
数据类型?
├── JSON / Object → 结构化克隆
├── 二进制 (音频/图像) → Transferable Objects
├── 共享状态 (游戏、仿真) → SharedArrayBuffer
| 特性 | Web Worker | WebAssembly | Service Worker |
|---|---|---|---|
| 用途 | 并行计算 | 高性能计算 | 离线缓存、代理 |
| DOM 访问 | ❌ | ❌ | ❌ |
| 生命周期 | 页面级 | 页面级 | 浏览器级 |
| 通信 | postMessage | JS 绑定 | postMessage |
| 共享数据 | Transferable/SAB | 线性内存 | Cache API |
| 适用场景 | CPU 密集型 | 性能关键型 | PWA、离线 |
// 浏览器端 NER (命名实体识别)
// worker.js
import { pipeline } from '@huggingface/transformers';
const ner = await pipeline(
'token-classification',
'Xenova/bert-base-NER',
{ quantized: true }
);
self.onmessage = async (e) => {
const { text, id } = e.data;
const entities = await ner(text);
// 提取地理位置
const locations = entities
.filter(e => e.entity === 'B-LOC' || e.entity === 'I-LOC')
.map(e => e.word);
self.postMessage({ id, locations });
};
// image-worker.js
self.onmessage = async (e) => {
const { imageData, quality } = e.data;
// 使用 Canvas API 压缩 (Worker 中可用 OffscreenCanvas)
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');
// 压缩处理...
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality });
// 返回压缩后的数据
const arrayBuffer = await blob.arrayBuffer();
self.postMessage({ buffer: arrayBuffer }, [arrayBuffer]);
};
// worker.js
const PREFIX = `[Worker-${self.name || 'default'}]`;
console.log(`${PREFIX} Starting task...`);
| 最佳实践 | 说明 |
|---|---|
| 使用 Worker Pool | 避免频繁创建/销毁 |
| 大数据用 Transferable | 零拷贝传输 |
| 模型预加载 | Worker 启动时加载模型 |
| 错误处理 | 完善的错误捕获与恢复 |
| 进度反馈 | 长时间任务提供进度 |
| 合理 Worker 数量 | 根据 CPU 核心数调整 |
| 场景 | 主线程 | Web Worker | 提升 |
|---|---|---|---|
| 10万条数据排序 | 5s (卡顿) | 5.2s (流畅) | UI 响应 |
| AI 情感分析 | 2s 阻塞 | 2s + 流畅 | 用户体验 |
| 图片压缩 | 1s 阻塞 | 1s + 进度条 | 交互友好 |
研究时间: 2026-03-07
研究者: 小凯
标签: #WebWorker #JavaScript #多线程 #Transformers.js #性能优化 #生产环境
还没有人回复