Web Worker多线程编程
约 1688 字大约 6 分钟
web-workerjavascript
2025-07-31
概述
Web Worker 是浏览器提供的多线程 API,允许在后台线程中运行 JavaScript 代码,不阻塞主线程的 UI 渲染和用户交互。Worker 通过消息传递(Message Passing)与主线程通信,适用于 CPU 密集型计算、大数据处理、图像处理等场景。
Worker 类型总览
| 类型 | 关联页面 | 通信方式 | 典型用途 |
|---|---|---|---|
| Dedicated Worker | 单个页面 | postMessage | CPU 密集计算 |
| Shared Worker | 多个同源页面 | port.postMessage | 跨标签页状态共享 |
| Service Worker | 独立于页面 | postMessage + FetchEvent | 离线缓存、推送通知 |
1. Dedicated Worker
基本使用
// main.js — 主线程
const worker = new Worker('worker.js');
// 发送消息给 Worker
worker.postMessage({ type: 'compute', data: [1, 2, 3, 4, 5] });
// 接收 Worker 的消息
worker.onmessage = (event) => {
console.log('Result:', event.data);
};
// 错误处理
worker.onerror = (error) => {
console.error('Worker error:', error.message);
};
// 终止 Worker
worker.terminate();// worker.js — Worker 线程
self.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'compute') {
// CPU 密集型计算(不阻塞主线程)
const result = data.reduce((sum, n) => {
// 模拟耗时计算
for (let i = 0; i < 1000000; i++) { /* ... */ }
return sum + n * n;
}, 0);
self.postMessage({ type: 'result', value: result });
}
};
// Worker 内可用的 API
// fetch, IndexedDB, WebSocket, setTimeout/setInterval
// 不可用:DOM, window, document, parent内联 Worker(无需独立文件)
function createInlineWorker(fn) {
const blob = new Blob(
[`self.onmessage = function(e) { (${fn.toString()})(e) }`],
{ type: 'application/javascript' }
);
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
URL.revokeObjectURL(url); // 释放 URL
return worker;
}
const worker = createInlineWorker((event) => {
const result = event.data.map(x => x * x);
self.postMessage(result);
});
worker.postMessage([1, 2, 3, 4, 5]);
worker.onmessage = (e) => console.log(e.data); // [1, 4, 9, 16, 25]2. 消息传递机制
结构化克隆(Structured Clone)
// 可以传递的类型
worker.postMessage({
number: 42,
string: 'hello',
boolean: true,
array: [1, 2, 3],
object: { nested: { deep: true } },
date: new Date(),
regexp: /pattern/gi,
map: new Map([['a', 1]]),
set: new Set([1, 2, 3]),
arrayBuffer: new ArrayBuffer(8),
typedArray: new Float64Array([1.1, 2.2]),
blob: new Blob(['data']),
imageData: new ImageData(100, 100),
});
// 不能传递的类型(会抛出 DataCloneError)
// Function, DOM 节点, Error, Symbol, WeakMap, WeakSet3. Transferable Objects
对于大型二进制数据,使用 Transferable Objects 可以零拷贝传递,将所有权从一个线程转移到另一个线程。
// 创建大型数据
const buffer = new ArrayBuffer(1024 * 1024 * 100); // 100MB
const data = new Float64Array(buffer);
// 普通传递 — 复制 100MB(慢)
worker.postMessage(buffer);
// Transfer 传递 — 零拷贝(快)
worker.postMessage(buffer, [buffer]);
// 注意:传递后 buffer 在主线程变为空(byteLength === 0)
console.log(buffer.byteLength); // 0(已转移所有权)// Worker 端:处理完成后转移回来
// worker.js
self.onmessage = (event) => {
const buffer = event.data;
const view = new Float64Array(buffer);
// 处理数据...
for (let i = 0; i < view.length; i++) {
view[i] = Math.sqrt(view[i]);
}
// 转移回主线程
self.postMessage(buffer, [buffer]);
};4. SharedArrayBuffer 与 Atomics
SharedArrayBuffer 允许多个线程共享同一块内存,Atomics 提供原子操作避免竞态条件。
// 主线程
const shared = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 10);
const array = new Int32Array(shared);
// 所有 worker 共享同一块内存
worker1.postMessage(shared);
worker2.postMessage(shared);
// 使用 Atomics 进行原子操作
Atomics.store(array, 0, 42); // 原子写入
const value = Atomics.load(array, 0); // 原子读取
Atomics.add(array, 0, 1); // 原子加法
Atomics.compareExchange(array, 0, 42, 43); // CAS 操作使用 Atomics 实现互斥锁
// 简单的自旋锁
class SpinLock {
constructor(sharedArray, index) {
this.array = sharedArray;
this.index = index;
}
lock() {
while (Atomics.compareExchange(this.array, this.index, 0, 1) !== 0) {
// 自旋等待
Atomics.wait(this.array, this.index, 1);
}
}
unlock() {
Atomics.store(this.array, this.index, 0);
Atomics.notify(this.array, this.index, 1);
}
}
// 使用
const shared = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2);
const lockArray = new Int32Array(shared);
const lock = new SpinLock(lockArray, 0);
lock.lock();
// 临界区操作
lockArray[1] += 1;
lock.unlock();5. Shared Worker
// shared-worker.js
const connections = [];
self.onconnect = (event) => {
const port = event.ports[0];
connections.push(port);
port.onmessage = (e) => {
// 广播给所有连接
connections.forEach(p => {
p.postMessage({ from: 'shared-worker', data: e.data });
});
};
port.start();
};// page-a.js 和 page-b.js(不同标签页)
const worker = new SharedWorker('shared-worker.js');
worker.port.onmessage = (event) => {
console.log('Received:', event.data);
};
worker.port.start();
worker.port.postMessage('Hello from page');6. Comlink 库
Comlink 是 Google 开发的库,将 Worker 通信封装为 RPC 调用,极大简化使用体验。
// worker.js
import * as Comlink from 'comlink';
const api = {
async heavyCompute(data) {
// CPU 密集型计算
return data.map(x => Math.sqrt(x));
},
counter: 0,
increment() {
return ++this.counter;
}
};
Comlink.expose(api);// main.js
import * as Comlink from 'comlink';
async function main() {
const worker = new Worker('worker.js', { type: 'module' });
const api = Comlink.wrap(worker);
// 像调用本地函数一样调用 Worker 方法
const result = await api.heavyCompute([1, 4, 9, 16]);
console.log(result); // [1, 2, 3, 4]
await api.increment();
await api.increment();
console.log(await api.counter); // 2
// 传递回调函数
await api.processWithCallback(
Comlink.proxy((progress) => {
console.log(`Progress: ${progress}%`);
})
);
}
main();7. Worker 线程池
class WorkerPool {
constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
this.workers = [];
this.taskQueue = [];
this.freeWorkers = [];
for (let i = 0; i < poolSize; i++) {
const worker = new Worker(workerScript);
this.workers.push(worker);
this.freeWorkers.push(worker);
}
}
execute(data) {
return new Promise((resolve, reject) => {
const task = { data, resolve, reject };
const worker = this.freeWorkers.pop();
if (worker) {
this._runTask(worker, task);
} else {
this.taskQueue.push(task); // 排队等待空闲 Worker
}
});
}
_runTask(worker, task) {
worker.onmessage = (event) => {
task.resolve(event.data);
this._onWorkerFree(worker);
};
worker.onerror = (error) => {
task.reject(error);
this._onWorkerFree(worker);
};
worker.postMessage(task.data);
}
_onWorkerFree(worker) {
const nextTask = this.taskQueue.shift();
if (nextTask) {
this._runTask(worker, nextTask);
} else {
this.freeWorkers.push(worker);
}
}
terminate() {
this.workers.forEach(w => w.terminate());
}
}
// 使用
const pool = new WorkerPool('compute-worker.js', 4);
const results = await Promise.all([
pool.execute({ task: 'sort', data: largeArray1 }),
pool.execute({ task: 'sort', data: largeArray2 }),
pool.execute({ task: 'sort', data: largeArray3 }),
// 即使有 100 个任务,也只用 4 个 Worker 处理
]);8. 实际应用场景
// 图像处理示例
// worker.js
self.onmessage = (event) => {
const { imageData, filter } = event.data;
const data = imageData.data;
if (filter === 'grayscale') {
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // R
data[i + 1] = avg; // G
data[i + 2] = avg; // B
// data[i + 3] Alpha 不变
}
}
self.postMessage({ imageData }, [imageData.data.buffer]);
};总结
Web Worker 为 JavaScript 带来了真正的多线程能力,使 CPU 密集型任务不再阻塞主线程。Dedicated Worker 处理单页面计算任务,Shared Worker 实现跨标签页通信,Service Worker 支持离线缓存和推送通知。通过 Transferable Objects 实现零拷贝传输大数据,SharedArrayBuffer 和 Atomics 提供共享内存和原子操作。Comlink 等库封装了底层消息传递,提供了更友好的 API。在实际项目中应结合 Worker 线程池管理资源,避免创建过多 Worker 实例。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于