HTML Web Workers:让 JavaScript“多线程”的指南
61
0
0
一句话:Worker 把耗时任务扔到后台线程,主页面继续流畅滚动,还能实时拿结果。下面从“建、通、关”到“传大文件、跨线程”一次讲透,复制即可跑。
1. 为什么需要 Worker?
JavaScript 是单线程,死循环 1 秒 = 页面卡 1 秒。
Web Workers 提供真正的操作系统级线程,特点:
- 与主线程并行,不阻塞 UI
- 无法操作 DOM(线程安全)
- 同源策略,必须通过消息通信
- 支持 ES6+、import 模块、fetch、IndexedDB 等
适用场景:
大量计算、图像/音频处理、轮询 WebSocket、加密/压缩、预加载
2. 30 秒搭一个“Hello Worker”
main.js(主线程)
const worker = new Worker('./worker.js'); // 1. 新建线程
worker.postMessage({ num: 42 }); // 2. 发数据
worker.onmessage = (e) => { // 3. 收结果
console.log('来自Worker:', e.data);
};
worker.onerror = (err) => console.error(err);worker.js(后台线程)
// self 代表 Worker 全局
self.onmessage = (e) => {
const result = e.data.num * 2; // 假装算很久
self.postMessage(result); // 把结果送回主线程
};文件必须同源且通过 HTTP/S 打开;双击本地 HTML 会报跨域 。
3. 消息通信格式:想传啥就传啥(几乎)
主线程 ↔ Worker 之间结构化克隆(Structured Clone),支持:
- 基本类型、对象、数组、Map、Set
- File、Blob、ArrayBuffer、ImageData
- 循环引用也没事;函数、DOM 节点会报错
示例:一次扔 100MB 大文件也 OK
worker.postMessage({ file: bigFileBlob }); // 主线程4. 实战 1:秒算斐波那契(再也不卡页面)
index.html
<input id="n" type="number" value="40">
<button onclick="calc()">普通算</button>
<button onclick="calcWorker()">Worker算</button>
<div id="result"></div>main.js
function calc() { // 主线程直接算——必卡
const res = fibonacci(+n.value);
result.textContent = res;
}
function calcWorker() {
const worker = new Worker('./fib-worker.js');
worker.postMessage(+n.value);
worker.onmessage = e => {
result.textContent = e.data;
worker.terminate(); // 算完即毁,省内存
};
}
function fibonacci(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); }fib-worker.js
self.onmessage = (e) => {
const n = e.data;
const res = fib(n); // 同逻辑,但在后台
self.postMessage(res);
};
function fib(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }点“Worker算”时输入框仍能响应,UI 帧率 60 FPS 。
5. 实战 2:多文件并行哈希(import 外部库)
worker.js
importScripts('https://cdn.jsdelivr.net/npm/crypto-js@4/crypto-js.js'); // 引入第三方库
self.onmessage = async (e) => {
const file = e.data; // File 对象
const buffer = await file.arrayBuffer();
const hash = CryptoJS.SHA256(buffer).toString();
self.postMessage({ name: file.name, hash });
};主线程循环发送即可并行计算,CPU 多核吃满。
6. 共享内存 SharedArrayBuffer(高阶可选)
// main.js
const sab = new SharedArrayBuffer(1024); // 1KB 共享内存
const worker = new Worker('worker.js');
worker.postMessage(sab);
// worker 直接读写同一块内存,零拷贝,游戏物理引擎利器因安全攻击(Spectre),目前需 跨源隔离(COOP/COEP 头)才能开启 ;普通业务先不用。
7. Worker 生命周期 & 调试
- 新建 →
new Worker(url) - 运行 → 执行顶层代码;
onmessage等待数据 - 终止 →
worker.terminate()立即杀线程(无法复活) - 调试 → Chrome DevTools → Sources → Worker threads 可断点
8. 异常与性能坑
| 坑 | 说明 | 急救 |
|---|---|---|
| 跨域加载 | file:/// 协议会报错 | 用 http(s):// 本地服务器 |
| 大对象复制 | 100MB 深克隆会卡一下 | 改用 Transferable(零拷贝) |
| 内存泄漏 | 只 new 不 terminate | 算完即毁,或复用单例 Worker |
| DOM 操作 | 在 Worker 里写 document 直接抛错 | 把结果发回主线程再改 DOM |
9. 现代化:模块 Worker(import/export)
// main.js
const worker = new Worker('./calc.js', { type: 'module' }); // 关键:type='module'
// calc.js
import { sha256 } from './hash.js'; // 现在能用 ESModule 了
export default self.onmessage = async (e) => { … };2024 年主流浏览器已支持,开发阶段直接 ESM,打包阶段可转 iife 兼容旧浏览器。
10. 一句话口诀(背完走天下)
“new 出线程,postMessage 传,onmessage 收,算完 terminate 清;
文件大用 Transferable,ESModule 写现代码;
不碰 DOM 零跨域,内存泄漏要牢记。”
掌握这 10 行心法,下次产品说“前端要算 10 万条哈希”——你就能淡定回答:
“上 Worker,页面不会卡。”
0
快来点个赞吧
发表评论
评论列表