Web Worker 多线程优化
Web Worker让JavaScript支持多线程执行,将耗时任务移出主线程,避免阻塞UI渲染。
核心概念
主线程限制
JavaScript
// ❌ 主线程执行耗时任务
function heavyCalculation() {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += Math.sqrt(i);
}
return result;
}
// 执行时页面卡死,无法响应用户交互
const result = heavyCalculation();
Worker架构
JavaScript
主线程(UI线程)
↓ 创建
Worker线程(计算线程) → 并行执行
↑ 通信
消息传递(postMessage/onmessage)
Worker特点
- 独立线程:不阻塞主线程
- 受限环境:无法访问DOM、window对象
- 消息通信:通过postMessage传递数据
- 脚本隔离:独立作用域,无共享状态
基础用法
创建Worker
JavaScript
// 主线程
const worker = new Worker('worker.js');
// 发送消息
worker.postMessage({ type: 'calculate', data: { n: 1000000 } });
// 接收消息
worker.onmessage = (event) => {
console.log('Worker result:', event.data);
};
// 错误处理
worker.onerror = (error) => {
console.error('Worker error:', error.message);
};
// 终止Worker
worker.terminate();
Worker脚本
JavaScript
// worker.js
self.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'calculate') {
const result = heavyCalculation(data.n);
self.postMessage({ type: 'result', result });
}
};
function heavyCalculation(n) {
let result = 0;
for (let i = 0; i < n; i++) {
result += Math.sqrt(i);
}
return result;
}
// Worker内部可以使用
// - setTimeout/setInterval
// - fetch/XMLHttpRequest
// - WebSocket
// - IndexedDB
// - console.log
内联Worker
JavaScript
// 使用Blob创建内联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, 5]);
worker.onmessage = (e) => console.log(e.data);
// 清理URL
URL.revokeObjectURL(workerUrl);
Worker通信机制
基本通信
JavaScript
// 主线程
const worker = new Worker('worker.js');
// 发送
worker.postMessage({
action: 'process',
payload: { data: [1, 2, 3] }
});
// 接收
worker.addEventListener('message', (event) => {
const response = event.data;
console.log('Response:', response);
});
// Worker线程
self.addEventListener('message', (event) => {
const { action, payload } = event.data;
if (action === 'process') {
const result = processData(payload.data);
self.postMessage({ action: 'result', result });
}
});
Transferable对象
JavaScript
// 大数据传输优化
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
// ❌ 默认拷贝传输
worker.postMessage(buffer); // 拷贝数据,耗时
// ✅ 转移所有权,零拷贝
worker.postMessage(buffer, [buffer]); // 转移后主线程无法访问buffer
// 示例:图像处理
const imageData = canvas.getImageData(0, 0, width, height);
worker.postMessage(imageData, [imageData.data.buffer]);
SharedArrayBuffer
JavaScript
// 共享内存(需特殊安全配置)
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
// 主线程和Worker共享同一内存
worker.postMessage({ buffer: sharedBuffer });
// Worker写入
sharedArray[0] = 100;
// 主线程读取
console.log(sharedArray[0]); // 100
// 同步操作使用Atomics
Atomics.store(sharedArray, 0, 200);
Atomics.load(sharedArray, 0); // 200
Worker类型
Dedicated Worker
JavaScript
// 专用Worker:只能被创建者使用
const worker = new Worker('dedicated.js');
worker.postMessage('hello');
Shared Worker
JavaScript
// 共享Worker:多个页面共享同一Worker
const worker = new SharedWorker('shared.js');
// 通过port通信
worker.port.onmessage = (event) => {
console.log('Shared worker message:', event.data);
};
worker.port.postMessage('hello from page A');
// shared.js
self.onconnect = (event) => {
const port = event.ports[0];
port.onmessage = (e) => {
port.postMessage('response');
};
};
Service Worker
JavaScript
// Service Worker:用于缓存和离线
// 注册
navigator.serviceWorker.register('/sw.js');
// sw.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then(cache => cache.addAll(['/']))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
实际应用场景
数据处理
JavaScript
// 主线程
const worker = new Worker('data-processor.js');
async function processLargeData(data) {
return new Promise((resolve) => {
worker.onmessage = (event) => {
resolve(event.data);
};
worker.postMessage(data);
});
}
// data-processor.js
self.onmessage = (event) => {
const data = event.data;
const processed = data.map(transform).filter(validate);
self.postMessage(processed);
};
function transform(item) {
// 复杂转换逻辑
return { ...item, processed: true };
}
图像处理
JavaScript
// 图像滤镜处理
const worker = new Worker('image-worker.js');
function applyFilter(imageData, filter) {
return new Promise((resolve) => {
worker.onmessage = (event) => {
resolve(event.data);
};
worker.postMessage({ imageData, filter }, [imageData.data.buffer]);
});
}
// image-worker.js
self.onmessage = (event) => {
const { imageData, filter } = event.data;
const processed = applyFilterToImageData(imageData, filter);
self.postMessage(processed, [processed.data.buffer]);
};
function applyFilterToImageData(imageData, filter) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// 灰度滤镜
if (filter === 'grayscale') {
const avg = (data[i] + data[i+1] + data[i+2]) / 3;
data[i] = data[i+1] = data[i+2] = avg;
}
}
return imageData;
}
加密解密
JavaScript
// 加密Worker
const cryptoWorker = new Worker('crypto-worker.js');
async function encryptData(data, key) {
return new Promise((resolve) => {
cryptoWorker.onmessage = (event) => {
resolve(event.data);
};
cryptoWorker.postMessage({ action: 'encrypt', data, key });
});
}
// crypto-worker.js
self.onmessage = async (event) => {
const { action, data, key } = event.data;
if (action === 'encrypt') {
const encrypted = await encrypt(data, key);
self.postMessage({ action: 'encrypted', result: encrypted });
}
};
async function encrypt(data, key) {
const algorithm = { name: 'AES-GCM', iv: crypto.getRandomValues(new Uint8Array(12)) };
const cryptoKey = await crypto.subtle.importKey('raw', key, algorithm, false, ['encrypt']);
const encrypted = await crypto.subtle.encrypt(algorithm, cryptoKey, data);
return encrypted;
}
大文件解析
JavaScript
// CSV/JSON大文件解析
const parserWorker = new Worker('parser-worker.js');
function parseLargeFile(file) {
return new Promise((resolve) => {
parserWorker.onmessage = (event) => {
resolve(event.data);
};
const reader = new FileReader();
reader.onload = (event) => {
parserWorker.postMessage({ content: event.target.result, type: file.type });
};
reader.readAsText(file);
});
}
// parser-worker.js
self.onmessage = (event) => {
const { content, type } = event.data;
if (type === 'text/csv') {
const rows = parseCSV(content);
self.postMessage(rows);
} else if (type === 'application/json') {
const data = JSON.parse(content);
self.postMessage(data);
}
};
function parseCSV(content) {
const lines = content.split('\n');
return lines.map(line => line.split(','));
}
Worker池管理
Worker池实现
JavaScript
class WorkerPool {
constructor(workerScript, poolSize = 4) {
this.workers = [];
this.taskQueue = [];
this.activeTasks = new Map();
for (let i = 0; i < poolSize; i++) {
const worker = new Worker(workerScript);
worker.id = i;
worker.onmessage = this.handleMessage.bind(this);
worker.onerror = this.handleError.bind(this);
this.workers.push({ worker, busy: false });
}
}
runTask(data) {
return new Promise((resolve, reject) => {
const availableWorker = this.workers.find(w => !w.busy);
if (availableWorker) {
this.executeTask(availableWorker, data, resolve, reject);
} else {
this.taskQueue.push({ data, resolve, reject });
}
});
}
executeTask(workerInfo, data, resolve, reject) {
workerInfo.busy = true;
const taskId = Date.now();
this.activeTasks.set(taskId, {
workerInfo,
resolve,
reject
});
workerInfo.worker.postMessage({ taskId, data });
}
handleMessage(event) {
const { taskId, result } = event.data;
const task = this.activeTasks.get(taskId);
if (task) {
task.workerInfo.busy = false;
task.resolve(result);
this.activeTasks.delete(taskId);
// 处理队列中的下一个任务
if (this.taskQueue.length > 0) {
const nextTask = this.taskQueue.shift();
this.executeTask(task.workerInfo, nextTask.data, nextTask.resolve, nextTask.reject);
}
}
}
handleError(error) {
console.error('Worker error:', error);
}
terminate() {
this.workers.forEach(w => w.worker.terminate());
}
}
// 使用
const pool = new WorkerPool('worker.js', 4);
const results = await Promise.all([
pool.runTask({ action: 'calc', data: 1 }),
pool.runTask({ action: 'calc', data: 2 }),
pool.runTask({ action: 'calc', data: 3 }),
]);
最佳实践
任务分片
JavaScript
// 大任务分片处理
function chunkedProcessing(worker, data, chunkSize = 1000) {
const chunks = [];
for (let i = 0; i < data.length; i += chunkSize) {
chunks.push(data.slice(i, i + chunkSize));
}
return Promise.all(
chunks.map(chunk => {
return new Promise(resolve => {
worker.onmessage = (event) => resolve(event.data);
worker.postMessage(chunk);
});
})
);
}
错误处理
JavaScript
// Worker错误捕获
worker.onerror = (error) => {
console.error('Worker error:', {
message: error.message,
filename: error.filename,
lineno: error.lineno
});
// 重启Worker
worker.terminate();
worker = new Worker('worker.js');
};
终止管理
text
// 页面卸载时终止Worker
window.addEventListener('beforeunload', () => {
worker.terminate();
});
// React组件清理
useEffect(() => {
const worker = new Worker('worker.js');
return () => {
worker.terminate();
};
}, []);
要点总结
- 独立线程:Worker不阻塞主线程,适合耗时任务
- 受限环境:无法访问DOM,只能通过消息通信
- Transferable:大数据使用转移所有权,零拷贝
- Worker池:管理多个Worker,队列分发任务
- 典型场景:数据处理、图像处理、加密、文件解析
- 生命周期:创建→通信→终止,及时清理
存放路径:articles/JS/专家/高级性能分析/Web Worker 多线程优化.md
📝 发现内容有误?点击此处直接编辑