Web Worker 是什么?如何使用?有什么限制?
Web Worker
JavaScript 是单线程的,Web Worker 允许在后台线程中运行 JS,不阻塞主线程。
基本使用
主线程:
// 创建 Worker
const worker = new Worker('worker.js')
// 发送数据
worker.postMessage({ command: 'calculate', data: [1, 2, 3] })
// 接收 Worker 返回的数据
worker.onmessage = (e) => {
console.log('结果:', e.data)
}
// 错误处理
worker.onerror = (e) => {
console.error('Worker error:', e.message)
}
// 终止
worker.terminate()Worker 线程(worker.js):
// 接收主线程数据
self.onmessage = (e) => {
const { command, data } = e.data
if (command === 'calculate') {
const result = heavyComputation(data)
// 发送结果回主线程
self.postMessage(result)
}
}
function heavyComputation(data) {
// 耗时计算...
return data.reduce((sum, n) => sum + n, 0)
}使用场景
- 大量计算:图像处理、加密、排序
- 数据处理:解析大 JSON、CSV
- 后台轮询:实时数据获取
- 离线缓存处理
// 内联 Worker(不需要单独文件)
const workerCode = `
self.onmessage = (e) => {
const result = e.data * 2
self.postMessage(result)
}
`
const blob = new Blob([workerCode], { type: 'application/javascript' })
const worker = new Worker(URL.createObjectURL(blob))SharedWorker(多标签页共享)
// 多个标签页共享同一个 Worker
const sharedWorker = new SharedWorker('shared-worker.js')
sharedWorker.port.onmessage = (e) => console.log(e.data)
sharedWorker.port.postMessage('hello')Worker 的限制
| 特性 | 支持 |
|---|---|
| DOM 操作 | ❌ |
| window 对象 | ❌(用 self) |
| XMLHttpRequest | ✅ |
| fetch | ✅ |
| WebSocket | ✅ |
| IndexedDB | ✅ |
| setTimeout/setInterval | ✅ |
| importScripts() | ✅(引入其他脚本) |
| console.log | ✅ |
| location/navigator | ✅(只读) |
Transferable Objects(零拷贝传输)
// ArrayBuffer 可以零拷贝转移(原线程不能再使用)
const buffer = new ArrayBuffer(1024 * 1024)
worker.postMessage(buffer, [buffer]) // 第二个参数是 transfer list
console.log(buffer.byteLength) // 0(已转移)性能注意
- Worker 创建有开销,不适合太短的任务
- postMessage 有序列化开销(Transferable Objects 除外)
- Worker 不能直接操作 DOM,必须通过 postMessage 通信
实际应用示例
// 大数据排序
worker.postMessage({ data: hugeArray })
// 主线程仍然流畅
// 实时图像处理
const canvas = document.querySelector('canvas')
const imageData = canvas.getContext('2d').getImageData(0, 0, w, h)
worker.postMessage(imageData, [imageData.data.buffer])
// Worker 处理后返回新的 imageData主线程替代方案
对于不需要真正多线程的场景,可以用以下方式避免阻塞:
requestIdleCallback:空闲时执行setTimeout(0):将任务拆分到不同事件循环Promise.resolve().then():微任务中执行
推荐场景
- 任务执行时间 > 100ms → 考虑 Worker
- 任务 < 50ms → 主线程即可