什么是 Web Workers?
Web Workers 是 HTML5 提供的一种浏览器多线程机制,允许你在后台线程中运行 JavaScript 脚本,而不会阻塞用户界面(主线程)。这意味着你可以执行耗时的计算任务(如大模型推理、图像处理、数据压缩等),同时保持页面的流畅响应。
✅ 核心目标: 解放主线程,提升用户体验。
为什么需要 Web Workers?
JavaScript 是单线程的,这意味着所有任务(DOM 操作、事件处理、网络请求、计算)都运行在一个线程上。
-
当执行一个耗时任务(比如训练模型、压缩图片、遍历大数据)时:
- 页面会“卡住”(无响应)
- 用户无法点击、滚动、输入
- 浏览器可能提示“页面未响应”
👉 Web Workers 就是为了解决这个问题而生的。
Web Workers 的特点
| 特性 | 说明 |
|---|---|
| 多线程支持 | 在浏览器中开启独立线程执行 JS |
| 不阻塞主线程 | 主线程继续响应用户操作 |
| 独立运行环境 | Worker 有自己独立的全局上下文(不能访问 DOM) |
| 通过消息通信 | 主线程与 Worker 之间通过 postMessage 和 onmessage 通信 |
| 受限权限 | 不能操作 DOM、不能使用 window、document 等对象 |
基本使用 API
1. 创建 Worker
// main.js - 主线程
const worker = new Worker('worker.js');
注意:Worker 文件必须是一个独立的
.js文件,且同源(出于安全考虑)。
2. 发送消息给 Worker
worker.postMessage('Hello Worker!');
// 也可以发送对象
worker.postMessage({ type: 'compress', data: imageData });
3. 接收来自 Worker 的消息
worker.onmessage = function(e) {
console.log('接收到结果:', e.data);
};
4. Worker 线程中(worker.js)
// worker.js
self.onmessage = function(e) {
console.log('收到消息:', e.data);
// 模拟耗时计算
const result = heavyComputation(e.data);
// 将结果返回主线程
self.postMessage(result);
};
function heavyComputation(data) {
// 比如:大模型推理、图像压缩、加密解密...
let sum = 0;
for (let i = 0; i < 1e9; i++) {
sum += i;
}
return sum;
}
5. 终止 Worker
worker.terminate(); // 立即终止
实际应用场景
1. 浏览器端运行大模型(端模型趋势)
随着 WebAssembly 和 WebGPU 的发展,越来越多的大模型(如 Llama、Bloom)开始支持在浏览器中运行。
- 使用 Web Worker 执行模型推理,避免页面卡顿。
- 模型加载、tokenization、推理都在 Worker 中完成。
- 主线程只负责 UI 更新。
🔮 趋势:AI in Browser(如 Hugging Face 的
transformers.js、Ollama Web、WebLLM)
2. 图片/视频压缩与处理
- 用户上传高清图 → Worker 中压缩 → 返回压缩结果
- 不影响用户继续操作页面
实战:用 Web Worker 实现图片压缩
我们来实现一个功能:用户上传图片 → 后台压缩 → 显示压缩结果,全程页面不卡顿。
项目结构如下:
project/
├── index.html // 页面结构
├── main.js // 主线程逻辑
└── compressWorker.js // 后台压缩线程
compressWorker.js→ Web Worker 线程(后台干活的)main.js→ 主线程 JavaScript(用户交互、启动压缩)index.html→ 页面结构(用户界面)
用户上传图片
↓
index.html → 触发 change 事件
↓
main.js → 读取文件为 base64 (FileReader)
↓
main.js → postMessage → compressWorker.js
↓
compressWorker.js → 接收 base64
↓
fetch + blob + createImageBitmap → 解码图像
↓
OffscreenCanvas + drawImage → 绘制
↓
convertToBlob({quality}) → 压缩
↓
FileReader → 转回 base64
↓
self.postMessage → 发回主线程
↓
main.js → onmessage → 收到压缩图
↓
innerHTML → 显示图片
1. 页面结构(index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片压缩</title>
</head>
<body>
<input type="file" id="fileInput" accept="image/*">
<div id="output"></div>
<script src="./main.js"></script>
</body>
</html>
input用于上传图片output用于显示压缩后的图片
2. 主线程控制(main.js)
// 启动一个 Web Worker
const worker = new Worker('./compressWorker.js');
// 接收 Worker 返回的结果
worker.onmessage = function(e) {
const output = document.getElementById('output');
if (e.data.success) {
output.innerHTML = `<img src="${e.data.data}" style="max-width:100%"/>`;
} else {
output.innerHTML = `<p style="color:red">压缩失败: ${e.data.error}</p>`;
}
};
// 读取文件为 base64
function handleFile(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(file);
});
}
// 发起压缩任务
async function compressFile(file) {
const imgDataUrl = await handleFile(file);
// 将图片数据发送给 Worker
worker.postMessage({
imgData: imgDataUrl,
quality: 0.3 // 压缩质量(0-1)
});
}
// 监听文件上传
document.getElementById('fileInput').addEventListener('change', async function(e) {
const file = e.target.files[0];
if (file) await compressFile(file);
});
📌 关键点:
- 使用
new Worker()启动后台线程 - 用
postMessage把图片数据发给 Worker - 用
onmessage接收压缩结果
3. 后台压缩逻辑(compressWorker.js)
self.onmessage = async function(e) {
const { imgData, quality = 0.8 } = e.data;
try {
// 1. base64 → Blob → ImageBitmap
const response = await fetch(imgData);
const blob = await response.blob();
const bitmap = await createImageBitmap(blob);
// 2. 创建 OffscreenCanvas(Worker 中可用的画布)
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0); // 绘制图像
// 3. 压缩:Canvas → Blob(JPEG + 质量控制)
const compressedBlob = await canvas.convertToBlob({
type: 'image/jpeg',
quality
});
// 4. Blob → base64(供主线程显示)
const reader = new FileReader();
reader.onloadend = () => {
self.postMessage({
success: true,
data: reader.result
});
};
reader.readAsDataURL(compressedBlob);
// 清理资源
bitmap.close();
} catch (err) {
self.postMessage({
success: false,
error: err.message
});
}
};
🚀 结尾
Web Worker 是每个前端开发者都应该掌握的进阶技能。它不仅是性能优化的利器,更是构建高性能 Web 应用(如在线 PS、AI 工具、数据可视化)的关键。
下次当你遇到“页面卡顿”问题时,不妨问自己:这个任务,能不能交给 Web Worker?