JavaScript Worker 相关知识点及用法详细解读
一、Worker 概述
1.1 什么是 Web Worker
Web Worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。它们可以在独立的线程中执行任务,避免阻塞主线程。
1.2 主要特点
- 独立线程运行:Worker 运行在与主线程分离的线程中
- 不阻塞主线程:适合执行密集型计算任务
- 无 DOM 访问权限:Worker 不能直接访问 DOM、window、document 等对象
- 通过消息通信:与主线程通过消息传递数据
- 支持多种类型:包括 Dedicated Worker、Shared Worker、Service Worker
二、Worker 类型
2.1 专用 Worker (Dedicated Worker)
// 主线程
const worker = new Worker('worker.js');
// 只能被创建它的脚本使用
// 生命周期与创建它的页面绑定
2.2 共享 Worker (Shared Worker)
// 主线程 - 页面1
const worker = new SharedWorker('shared-worker.js');
// 主线程 - 页面2(同一域名下)
const worker2 = new SharedWorker('shared-worker.js');
// 多个页面可以共享同一个 Worker
// 通过 port 进行通信
2.3 Service Worker
// 主线程
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(registration => {
console.log('Service Worker 注册成功:', registration);
});
}
// 主要用于离线缓存、推送通知、后台同步等
三、基本用法
3.1 创建和使用 Worker
主线程代码 (main.js):
// 创建 Worker
const worker = new Worker('worker.js');
// 向 Worker 发送消息
worker.postMessage({
type: 'CALCULATE',
data: { numbers: [1, 2, 3, 4, 5] }
});
// 接收 Worker 的消息
worker.onmessage = function(event) {
console.log('收到 Worker 消息:', event.data);
if (event.data.type === 'RESULT') {
console.log('计算结果:', event.data.result);
}
};
// 错误处理
worker.onerror = function(error) {
console.error('Worker 错误:', error);
};
// 终止 Worker
// worker.terminate();
Worker 线程代码 (worker.js):
// 接收主线程消息
self.onmessage = function(event) {
const { type, data } = event.data;
if (type === 'CALCULATE') {
// 执行计算(不会阻塞主线程)
const result = heavyCalculation(data.numbers);
// 发送结果回主线程
self.postMessage({
type: 'RESULT',
result: result
});
}
};
function heavyCalculation(numbers) {
// 模拟耗时计算
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
// 模拟复杂计算
for (let j = 0; j < 1000000; j++) {
// 密集计算
}
}
return sum;
}
// 也可以使用 addEventListener
// self.addEventListener('message', handleMessage);
3.2 传递复杂数据
结构化克隆算法:
// Worker 支持传递大多数 JavaScript 类型
const complexData = {
string: '文本',
number: 123,
boolean: true,
array: [1, 2, 3],
object: { key: 'value' },
date: new Date(),
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
typedArray: new Uint8Array([1, 2, 3]),
blob: new Blob(['Hello World'], { type: 'text/plain' }),
arrayBuffer: new ArrayBuffer(8)
};
// 但以下类型不能传递:
// - 函数
// - DOM 节点
// - 原型链
// - Error 和 Function 对象
// - 某些特定对象(如 Promise)
worker.postMessage(complexData);
Transferable Objects(可转移对象):
// 创建 ArrayBuffer
const buffer = new ArrayBuffer(1024 * 1024 * 10); // 10MB
// 使用可转移对象,避免复制大数据
worker.postMessage(
{ buffer: buffer },
[buffer] // 第二个参数指定可转移对象
);
// 注意:转移后,主线程不能再访问 buffer
// buffer.byteLength === 0
四、高级用法
4.1 动态创建 Worker
使用 Blob URL 创建内联 Worker:
// 将 Worker 代码作为字符串
const workerCode = `
self.onmessage = function(e) {
const result = e.data.num * 2;
self.postMessage({ result: result });
};
`;
// 创建 Blob
const blob = new Blob([workerCode], { type: 'application/javascript' });
const workerURL = URL.createObjectURL(blob);
// 创建 Worker
const worker = new Worker(workerURL);
// 使用后记得清理
worker.onmessage = function(e) {
console.log('结果:', e.data.result);
URL.revokeObjectURL(workerURL); // 清理 URL
};
4.2 模块化 Worker
ES6 模块支持:
// 主线程
const worker = new Worker('module-worker.js', {
type: 'module' // 指定为模块
});
// Worker 线程 (module-worker.js)
import { calculate } from './calculations.js';
self.onmessage = function(e) {
const result = calculate(e.data);
self.postMessage(result);
};
// 注意:需要服务器支持 CORS 和正确的 MIME 类型
4.3 Worker 中使用其他 Worker
// Worker 中也可以创建 Worker(子 Worker)
// worker.js
const subWorker = new Worker('sub-worker.js');
subWorker.onmessage = function(e) {
// 处理子 Worker 的结果
self.postMessage({
type: 'FROM_SUB_WORKER',
data: e.data
});
};
self.onmessage = function(e) {
// 将任务分配给子 Worker
subWorker.postMessage(e.data);
};
五、共享 Worker
5.1 创建和连接共享 Worker
主线程代码:
// 创建或连接到共享 Worker
const worker = new SharedWorker('shared-worker.js');
// 通过 port 进行通信
worker.port.onmessage = function(event) {
console.log('收到共享 Worker 消息:', event.data);
};
// 启动端口连接
worker.port.start();
// 发送消息
worker.port.postMessage({
type: 'GREETING',
message: 'Hello from page!'
});
// 断开连接
// worker.port.close();
共享 Worker 代码 (shared-worker.js):
// 存储所有连接的端口
const ports = [];
// 监听连接
self.onconnect = function(event) {
const port = event.ports[0];
ports.push(port);
port.onmessage = function(event) {
const message = event.data;
// 广播消息给所有连接的端口
if (message.type === 'BROADCAST') {
ports.forEach(p => {
if (p !== port) {
p.postMessage({
type: 'BROADCAST_MESSAGE',
data: message.data,
from: 'Shared Worker'
});
}
});
}
// 响应特定消息
port.postMessage({
type: 'RESPONSE',
data: 'Message received by Shared Worker'
});
};
// 启动端口
port.start();
};
5.2 共享 Worker 应用场景
- 多个标签页/窗口间的通信
- 共享计算资源
- 跨页面状态同步
- 共享 WebSocket 连接
六、Service Worker
6.1 注册和安装
主线程注册:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
console.log('ServiceWorker 注册成功: ', registration.scope);
// 检查更新
registration.addEventListener('updatefound', () => {
console.log('发现新版本 Service Worker');
});
})
.catch(function(err) {
console.log('ServiceWorker 注册失败: ', err);
});
});
}
Service Worker 代码 (service-worker.js):
// 定义缓存名称
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
// 安装阶段
self.addEventListener('install', function(event) {
// 跳过等待,立即激活
// self.skipWaiting();
// 缓存资源
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('已打开缓存');
return cache.addAll(urlsToCache);
})
);
});
// 激活阶段
self.addEventListener('activate', function(event) {
// 清理旧缓存
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
// 立即控制所有客户端
// event.waitUntil(self.clients.claim());
});
// 拦截请求
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// 缓存命中
if (response) {
return response;
}
// 克隆请求(因为请求是流,只能使用一次)
const fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// 检查响应是否有效
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应(因为响应是流,只能使用一次)
const responseToCache = response.clone();
// 缓存新资源
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
// 推送通知
self.addEventListener('push', function(event) {
const title = '推送通知标题';
const options = {
body: '推送通知内容',
icon: '/images/icon.png',
badge: '/images/badge.png'
};
event.waitUntil(
self.registration.showNotification(title, options)
);
});
// 通知点击
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(
clients.openWindow('/')
);
});
6.2 Service Worker 生命周期
下载 →
安装 →
激活 →
闲置 →
终止
默认情况下,需要关闭所有使用旧 Service Worker 的页面,新版本才会激活
可以通过
skipWaiting() 和
clients.claim() 强制立即激活
七、Worker 中的 API 限制
7.1 可用的 API
XMLHttpRequest, fetch()(网络请求)
setTimeout(), setInterval()
WebSocket
IndexedDB
Web Workers(创建子 Worker)
importScripts()(非模块 Worker)
BroadcastChannel
Cache API(Service Worker)
7.2 不可用的 API
- DOM 操作(document, window)
alert(), confirm(), prompt()
- 某些 Web API(如 Geolocation)
- 同步 XHR(已废弃)
八、最佳实践和注意事项
8.1 性能优化
// 1. 使用 Transferable Objects 传输大数据
const buffer = new ArrayBuffer(1000000);
worker.postMessage({ buffer }, [buffer]);
// 2. 批量处理消息,避免频繁通信
let batch = [];
function processBatch() {
if (batch.length > 0) {
worker.postMessage({ batch });
batch = [];
}
}
// 3. 合理终止 Worker
function terminateWorkerIfIdle(worker, timeout = 60000) {
const timer = setTimeout(() => {
worker.terminate();
console.log('Worker 已终止(空闲超时)');
}, timeout);
worker.onmessage = function() {
clearTimeout(timer);
// 重置计时器
terminateWorkerIfIdle(worker, timeout);
};
}
8.2 错误处理
// 主线程
worker.onerror = function(error) {
console.error('Worker 错误:', error);
// 记录错误信息
const errorInfo = {
filename: error.filename,
lineno: error.lineno,
colno: error.colno,
message: error.message
};
// 重新启动 Worker(根据情况)
if (shouldRestart(error)) {
restartWorker();
}
};
// Worker 内部错误处理
try {
// 可能出错的代码
riskyOperation();
} catch (error) {
// 发送错误信息到主线程
self.postMessage({
type: 'ERROR',
error: {
message: error.message,
stack: error.stack
}
});
}
8.3 调试技巧
// 1. 使用 console(在浏览器开发者工具的 Worker 面板查看)
console.log('Worker 日志');
console.error('Worker 错误');
// 2. 发送调试信息到主线程
self.postMessage({
type: 'DEBUG',
data: {
state: 'processing',
progress: 50,
details: '正在处理数据...'
}
});
// 3. 使用调试器语句
debugger; // 会在浏览器开发者工具中暂停
九、实际应用示例
9.1 图像处理 Worker
// 主线程
const imageWorker = new Worker('image-processor.js');
function processImage(imageData) {
return new Promise((resolve, reject) => {
imageWorker.onmessage = function(e) {
if (e.data.type === 'SUCCESS') {
resolve(e.data.processedData);
} else if (e.data.type === 'ERROR') {
reject(new Error(e.data.message));
}
};
imageWorker.postMessage({
type: 'PROCESS_IMAGE',
imageData: imageData,
operations: ['grayscale', 'blur', 'resize']
});
});
}
// 使用
canvas.toBlob(function(blob) {
processImage(blob)
.then(processedBlob => {
// 更新图像显示
})
.catch(error => {
console.error('图像处理失败:', error);
});
});
9.2 实时数据处理
// data-processor.js
let dataBuffer = [];
let processing = false;
self.onmessage = function(e) {
if (e.data.type === 'DATA_STREAM') {
// 收集数据
dataBuffer.push(...e.data.chunk);
// 批量处理
if (!processing && dataBuffer.length >= 1000) {
processing = true;
processBatch();
}
}
};
function processBatch() {
if (dataBuffer.length === 0) {
processing = false;
return;
}
// 处理数据(不会阻塞主线程)
const batch = dataBuffer.splice(0, 1000);
const result = performHeavyProcessing(batch);
// 发送结果
self.postMessage({
type: 'PROCESSED_DATA',
data: result
});
// 继续处理下一批
setTimeout(processBatch, 0);
}
十、兼容性和注意事项
10.1 浏览器支持
- Web Workers: IE10+、所有现代浏览器
- Shared Workers: 除 IE、Safari 外的大部分现代浏览器
- Service Workers: Chrome 40+、Firefox 44+、Edge 17+
10.2 检测支持
// 检测 Worker 支持
if (typeof Worker !== 'undefined') {
// 支持 Web Worker
}
// 检测 SharedWorker 支持
if (typeof SharedWorker !== 'undefined') {
// 支持 SharedWorker
}
// 检测 Service Worker 支持
if ('serviceWorker' in navigator) {
// 支持 Service Worker
}
10.3 安全限制
- 同源策略: Worker 脚本必须与主页面同源(或支持 CORS)
- 文件协议: 本地文件可能受限(file:// 协议)
- HTTPS: Service Worker 要求 HTTPS(localhost 除外)
总结
Web Worker 为 JavaScript 提供了真正的多线程能力,是优化 Web 应用性能的重要工具。合理使用 Worker 可以将耗时任务从主线程分离,保持 UI 的流畅响应。根据不同的需求,可以选择合适的 Worker 类型:
- 专用 Worker:处理独立的后台任务
- 共享 Worker:实现多个页面间的通信和资源共享
- Service Worker:实现离线功能、推送通知等高级特性
正确使用 Worker 需要考虑通信开销、内存管理和错误处理等因素,但在适当的场景下,它能显著提升应用性能和用户体验。