Web Workers API
一、为什么需要 Web Workers
1.1 JavaScript 单线程的问题
JavaScript 在主线程运行,复杂的计算会阻塞 UI 响应。用户会感觉页面"卡住"了。
1.2 Web Workers 的解决方案
Web Workers 是在后台运行的独立线程,不会阻塞 UI:
- 计算密集型任务可以在 Worker 中执行
- 主线程保持响应
- 通过消息传递与主线程通信
二、基本用法
2.1 创建 Dedicated Worker
// 主线程
const worker = new Worker('worker.js');
// 发送消息到 Worker
worker.postMessage({ type: 'START', data: someData });
// 接收 Worker 的消息
worker.onmessage = function(event) {
console.log('收到 Worker 消息:', event.data);
};
// 错误处理
worker.onerror = function(error) {
console.error('Worker 错误:', error);
};
// worker.js
self.onmessage = function(event) {
const { type, data } = event.data;
if (type === 'START') {
// 执行计算
const result = processData(data);
// 发送结果回主线程
self.postMessage({ result });
}
};
function processData(data) {
// 耗时计算
return data.map(item => item * 2);
}
2.2 terminate 和 close
// 主线程:终止 Worker
worker.terminate();
// Worker 自身:关闭自己
self.close();
三、Dedicated vs Shared vs Service Worker
3.1 Dedicated Worker
专用 Worker,只能被创建它的主线程使用:
// 只能当前脚本使用
const worker = new Worker('worker.js');
3.2 Shared Worker
共享 Worker,可以被多个脚本使用:
// 主线程 1
const sharedWorker = new SharedWorker('shared-worker.js');
// 主线程 2
const sharedWorker2 = new SharedWorker('shared-worker.js');
// shared-worker.js
self.onconnect = function(event) {
const port = event.ports[0];
port.onmessage = function(event) {
console.log('收到消息:', event.data);
};
port.postMessage('你好');
};
3.3 与 Service Worker 的区别
| 特性 | Dedicated Worker | Shared Worker | Service Worker |
|---|---|---|---|
| 作用域 | 单页面 | 多页面 | 多页面 |
| 生命周期 | 页面关闭即结束 | 最后使用页面关闭 | 独立于页面 |
| 主要用途 | 计算密集任务 | 跨页面通信 | 网络拦截、推送 |
| 访问 API | 有限 | 有限 | 缓存、推送 |
四、importScripts 和 ES Module Worker
4.1 importScripts
传统 Worker 使用 importScripts 导入脚本:
// worker.js
importScripts('utils.js', 'constants.js');
// 然后使用导入的函数
const result = processData(input);
4.2 ES Module Worker
现代浏览器支持 ES Module Worker:
// 主线程
const worker = new Worker('worker.js', { type: 'module' });
// worker.js
import { helper } from './utils.js';
self.onmessage = function(event) {
const result = helper(event.data);
self.postMessage(result);
};
五、Comlink
5.1 简化 Worker 通信
Comlink 是一个库,简化了 Worker 的消息传递:
// worker.js
import * as Comlink from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
const workerAPI = {
async processData(data) {
return data.map(item => item * 2);
}
};
Comlink.expose(workerAPI, self);
// 主线程
const worker = new Worker('worker.js', { type: 'module' });
const api = Comlink.wrap(worker);
// 像调用本地函数一样调用
const result = await api.processData([1, 2, 3]);
六、OffscreenCanvas
6.1 在 Worker 中绘制
OffscreenCanvas 允许在 Worker 中进行 Canvas 绘制:
// 主线程
const canvas = document.getElementById('myCanvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('canvas-worker.js', { type: 'module' });
worker.postMessage({ canvas: offscreen }, [offscreen]);
// canvas-worker.js
let ctx;
self.onmessage = function(event) {
const canvas = event.data.canvas;
ctx = canvas.getContext('2d');
// 绘制
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 100, 100);
};
七、适合 Worker 的计算
适合在 Worker 中执行的任务:
- JSON 序列化/反序列化
- 图像处理和滤镜
- 视频/音频编解码
- 大数据排序和搜索
- 加密解密
- 复杂数学计算
不适合在 Worker 中执行的任务:
- 需要频繁 DOM 操作的
- 需要 UI 更新的
- 小数据量的简单计算