Web Worker 基本使用
前言
在Web开发中,JavaScript的单线程特性常常成为性能瓶颈。当我们遇到需要大量计算的场景时(如图像处理、数据分析等),Web Worker 就成为了关键的解决方案。本文将通过一个完整示例,带你快速掌握Web Worker的使用方法。
一、什么是Web Worker?
Web Worker是浏览器提供的JavaScript多线程解决方案,它允许我们在后台线程中运行脚本,与主线程并行执行。关键特性:
- 独立全局上下文
- 无法直接操作DOM
- 通过消息机制通信
- 支持传递可序列化数据
什么时候用 Web Worker?
适合:
- 计算密集型任务(如加密、数据处理)
- 长时间运行的任务(如 WebSockets、消息队列)
- 渲染优化(如复杂动画、WebGL)
不适合:
- 需要操作 DOM
- 轻量级任务(可能带来额外线程管理成本)
二、快速创建Web Worker
1. 项目结构
2.样式文件(index.html)
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Worker 调试演示</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #f0f2f5;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover {
background-color: #0056b3;
}
#output {
margin-top: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
min-height: 100px;
}
</style>
</head>
<body>
<div class="container">
<h1>Web Worker 调试演示</h1>
<button onclick="startWorker()">启动Worker</button>
<button onclick="sendMessage()">发送消息</button>
<button onclick="stopWorker()">停止Worker</button>
<div id="output"></div>
</div>
<script src="./main.js"></script>
</body>
</html>
3. 主线程代码(main.js)
javascript
let worker;
function log (message) {
document.getElementById('output').innerHTML += `<div>${new Date().toLocaleTimeString()}: ${message}</div>`;
}
function startWorker () {
if (typeof (Worker) === 'undefined')
{
log('浏览器不支持Web Workers');
return;
}
if (!worker)
{
worker = new Worker('./worker.js');
// 接收Worker消息
worker.onmessage = function (e) {
log(`收到Worker消息: ${JSON.stringify(e.data)}`);
};
// 错误处理
worker.onerror = function (e) {
log(`Worker错误: ${e.message} (行号:${e.lineno})`);
};
log('Worker已启动');
}
}
function sendMessage () {
if (worker)
{
const message = {
type: 'CALCULATE',
data: Math.floor(Math.random() * 100)
};
worker.postMessage(message);
log(`已发送消息: ${JSON.stringify(message)}`);
}
}
function stopWorker () {
if (worker)
{
worker.terminate();
worker = null;
log('Worker已停止');
}
}
4. Worker线程代码(worker.js)
php
// 监听主线程消息
// self 代表当前 Worker 线程的全局对象(类似于主线程中的 window)。
self.onmessage = function (e) {
console.log('Worker收到消息:', e.data);
switch (e.data.type)
{
case 'CALCULATE':
const result = fibonacci(e.data.data);
self.postMessage({
type: 'RESULT',
input: e.data.data,
result: result
});
break;
default:
self.postMessage({
type: 'ERROR',
message: '未知的消息类型'
});
}
}
// 模拟计算密集型任务
function fibonacci (n) {
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++)
{
[a, b] = [b, a + b];
}
return b;
}
// 错误处理
self.onerror = function (e) {
console.error('Worker内部错误:', e);
self.postMessage({
type: 'ERROR',
message: e.message,
stack: e.error?.stack
});
}
// 调试用定时器
let debugCounter = 0;
setInterval(() => {
console.log('Worker运行中:', debugCounter++);
self.postMessage({
type: 'DEBUG',
counter: debugCounter
});
}, 5000);
5. 创建npm项目配置文件,定义调试服务器启动脚本。
bash
{
"name": "worker-demo",
"version": "1.0.0",
"scripts": {
"start": "http-server -p 8080",
"test": "echo "Error: no test specified" && exit 1"
},
"devDependencies": {
"http-server": "^14.1.1"
}
}
6.功能演示
- 点击【启动 Worker】初始化
- 点击【发送消息】触发斐波那契数列计算
- 观察 Worker 定时发送的 debug 计数器
- 点击【停止 Worker】终止线程
三、核心API解析
-
创建Worker
iniconst worker = new Worker('worker.js');
-
消息传递
- 主线程发送:
worker.postMessage(data)
- Worker发送:
self.postMessage(data)
(1) 传递普通数据(字符串、数字、对象)
postMessage() 可以发送字符串、数字、数组、对象等:
phpworker.postMessage({ task: "sum", num: 100 });
(2) 传递 TypedArray(更快的数据处理)
适用于处理二进制数据,如 ArrayBuffer:
arduinoconst buffer = new ArrayBuffer(1024); worker.postMessage(buffer);
注意 :如果 postMessage() 传递 ArrayBuffer,默认会转移(Transferable Objects) ,主线程无法再访问原数据。
- 主线程发送:
-
消息接收
php// 主线程 worker.onmessage = function(e) { /* ... */ } // Worker线程 self.onmessage = function(e) { /* ... */ }
-
终止Worker
scssworker.terminate(); // 主线程调用 self.close(); // Worke
四. Worker 线程中的限制
- 不能操作 DOM(不能使用 document、window)
- 不能直接访问本地存储(localStorage、sessionStorage)
- 可以使用 fetch、WebSockets 等进行网络请求
- 可以使用 setTimeout / setInterval(但不推荐)