JavaScript Web Workers 与多线程
JavaScript 默认单线程执行,Web Workers 提供真正的多线程能力,在后台线程执行脚本,不阻塞主线程 UI。
Worker 基本用法
JavaScript
// 主线程:创建 Worker
const worker = new Worker('worker.js');
// 发送消息
worker.postMessage({ type: 'compute', data: [1, 2, 3, 4, 5] });
// 接收消息
worker.onmessage = (event) => {
console.log('Worker 结果:', event.data);
};
// 错误处理
worker.onerror = (error) => {
console.error('Worker 错误:', error.message);
};
// 终止 Worker
worker.terminate();
JavaScript
// worker.js:Worker 线程代码
// 监听主线程消息
self.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'compute') {
// 执行计算
const result = data.reduce((sum, n) => sum + n * n, 0);
// 发送结果回主线程
self.postMessage({ type: 'result', data: result });
}
};
// Worker 内错误处理
self.onerror = (error) => {
console.error('Worker 错误:', error);
};
内联 Worker
JavaScript
// Blob URL 创建内联 Worker(无需单独文件)
const workerCode = `
self.onmessage = (e) => {
const result = e.data.map(x => x * x);
self.postMessage(result);
};
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);
const worker = new Worker(workerUrl);
worker.postMessage([1, 2, 3, 4]);
worker.onmessage = (e) => console.log(e.data); // [1, 4, 9, 16]
// 清理 Blob URL
URL.revokeObjectURL(workerUrl);
worker.terminate();
数据传输方式
JavaScript
// 结构化克隆:默认方式,复制数据
const data = new Uint8Array([1, 2, 3, 4, 5]);
worker.postMessage(data); // 数据被复制,主线程仍可访问
console.log(data); // [1, 2, 3, 4, 5]
// Transferable 对象:转移所有权,零拷贝
const largeData = new Uint8Array(10000000);
worker.postMessage(largeData, [largeData.buffer]); // 转移 buffer
console.log(largeData.length); // 0(主线程不再拥有)
// worker.js
self.onmessage = (e) => {
const data = e.data;
// 直接使用转移的数据
console.log('收到数据:', data.length);
};
SharedArrayBuffer 共享内存
JavaScript
// 主线程:创建共享内存
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Uint8Array(sharedBuffer);
// 初始化数据
for (let i = 0; i < sharedArray.length; i++) {
sharedArray[i] = i;
}
// 发送到多个 Worker(共享同一内存)
const workers = [];
for (let i = 0; i < 4; i++) {
const worker = new Worker('shared-worker.js');
worker.postMessage({ buffer: sharedBuffer, workerId: i });
workers.push(worker);
}
// worker.js:操作共享内存
self.onmessage = (e) => {
const { buffer, workerId } = e.data;
const array = new Uint8Array(buffer);
// 原子操作确保安全
Atomics.add(array, workerId, 10);
self.postMessage({ workerId, done: true });
};
Worker 类型
JavaScript
// Dedicated Worker:专用 Worker,一对一通信
const dedicated = new Worker('worker.js');
// Shared Worker:共享 Worker,多页面共享
const shared = new SharedWorker('shared-worker.js');
shared.port.onmessage = (e) => console.log(e.data);
shared.port.postMessage('hello');
// Service Worker:缓存和离线支持
// 注册 Service Worker
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('Service Worker 注册成功');
});
// sw.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then(cache => cache.addAll(['/index.html', '/app.js']))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(response => response || fetch(event.request))
);
});
Worker 通信模式
JavaScript
// 单向消息
worker.postMessage('单向消息');
// 双向请求-响应
function workerRequest(worker, data) {
return new Promise((resolve) => {
const handler = (e) => {
if (e.data.id === data.id) {
worker.removeEventListener('message', handler);
resolve(e.data.result);
}
};
worker.addEventListener('message', handler);
worker.postMessage(data);
});
}
// 使用
const result = await workerRequest(worker, { id: 1, task: 'compute' });
// BroadcastChannel:跨 Worker 通信
const channel = new BroadcastChannel('worker-channel');
channel.postMessage({ from: 'main', data: 'hello' });
channel.onmessage = (e) => console.log('收到:', e.data);
// Worker 内也可以创建 BroadcastChannel
// worker.js
const channel = new BroadcastChannel('worker-channel');
channel.onmessage = (e) => {
console.log('Worker 收到:', e.data);
channel.postMessage({ from: 'worker', reply: '收到' });
};
计算密集型任务示例
JavaScript
// 主线程:大数据计算委托给 Worker
function parallelCompute(data, workerCount = 4) {
const chunkSize = Math.ceil(data.length / workerCount);
const workers = [];
const results = [];
return new Promise((resolve) => {
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('compute-worker.js');
const chunk = data.slice(i * chunkSize, (i + 1) * chunkSize);
worker.postMessage({ chunk, index: i });
worker.onmessage = (e) => {
results[e.data.index] = e.data.result;
if (results.length === workerCount && results.every(r => r !== undefined)) {
workers.forEach(w => w.terminate());
resolve(results.flat());
}
};
workers.push(worker);
}
});
}
// 使用
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
const result = await parallelCompute(largeArray);
JavaScript
// compute-worker.js
self.onmessage = (e) => {
const { chunk, index } = e.data;
// 执行计算
const result = chunk.map(x => x * x * x);
self.postMessage({ result, index });
};
Worker 线程限制
JavaScript
// Worker 内可用:
// - XMLHttpRequest / fetch
// - WebSocket
// - IndexedDB
// - setTimeout / setInterval
// - Atomics / SharedArrayBuffer
// - 其他 Worker(嵌套 Worker)
// Worker 内不可用:
// - DOM 操作(document, window)
// - parent 对象
// - location(可读但不可写)
// - localStorage / sessionStorage(可用 IndexedDB 替代)
// - alert / confirm / prompt
// - UI 相关 API
// Worker 内创建嵌套 Worker
const subWorker = new Worker('sub-worker.js');
subWorker.postMessage('from main worker');
注意事项
- Worker 有启动开销,适合长时间任务,短任务不值得
- 数据传输默认复制,大数据用 Transferable 零拷贝
- Worker 与主线程完全隔离,无 DOM 访问
- 创建过多 Worker 消耗资源,建议池化管理
JavaScript
// Worker 池
class WorkerPool {
constructor(workerScript, poolSize = 4) {
this.workers = Array.from({ length: poolSize }, () => new Worker(workerScript));
this.busy = new Set();
}
async execute(data) {
const available = this.workers.find(w => !this.busy.has(w));
if (!available) {
// 等待可用 Worker
return new Promise(resolve => {
this.queue.push({ data, resolve });
});
}
this.busy.add(available);
return new Promise(resolve => {
available.onmessage = (e) => {
this.busy.delete(available);
resolve(e.data);
if (this.queue.length) {
const next = this.queue.shift();
this.execute(next.data).then(next.resolve);
}
};
available.postMessage(data);
});
}
terminate() {
this.workers.forEach(w => w.terminate());
}
}
要点总结
- Web Workers 在独立线程执行,不阻塞主线程 UI
postMessage通信,onmessage接收,terminate终止- 数据传输默认复制,Transferable 实现零拷贝转移
- SharedArrayBuffer + Atomics 实现线程安全共享内存
- Worker 无 DOM 访问,可用 fetch、IndexedDB、WebSocket
- Shared Worker 多页面共享,Service Worker 缓存离线
- 适合计算密集型任务,短任务不适合(启动开销)
- 推荐使用 Worker 池管理多个 Worker
📝 发现内容有误?点击此处直接编辑