什么是webWorker
- webworker实现了前端开发的多线程开发,就是把js代码放到后台线程中跑
- 运行独立线程,不会互相影响
- 纯消息通知,如果用过websocket的话,使用过程中就会发现这个跟websocket类似的
-初始化webWorker
typescript
class DataMonitor {
private options: DataMonitorOptions;
private worker: Worker | null;
constructor(options: DataMonitorOptions) {
this.options = options;
this.worker = null;
}
// 初始化Web Worker
initWorker() {
try {
// 初始化Web Worker
this.worker = new Worker(new URL('./data-worker.ts', import.meta.url), { type: 'module' });
// 监听Worker消息
this.options.onLog('Worker初始化成功', 'success');
} catch (error: unknown) {
this.options.onLog(`Worker初始化失败: ${error instanceof Error ? error.message : String(error)}`, 'error');
}
}
}
- 这段代码就是newWorker的初始化,我在这里面传递了两个参数
- 第一个参数就是我要进行数据通信的文件
- 第二个参数中的type为module是为了让worker中使用import/export这种语句,并且可以导入其他的es5模块
进行通信
- 通信有两种通信情况
kotlin
- 第一种是我在DataMonitor中创建的方法,我会在外部通过事件触发的形式来触发这个方法,
然后通过里面 this.worker.postMessage({type: '...'})这里的type是自己定义的通信类型
,当我触发这个通信的时候就会通知data-worker.ts中的这个通信类型,然后根据这个类型去调用对应的方法
- 简单示例
addChat: (data: any) => { //这个是在DataMonitor类中定义的方法
if (this.worker) {
this.worker.postMessage({
type: 'getTopic',
})
}
}
//data-worker.ts
self.onmessage = function (event) {
switch (event.data.type) {
case 'addChat':
messageHandlers.addChat(event.data);
break;
}
}
php
- 第二种就是当我调用data-work中定义的方法后,如果成功或者失败如何通知主线程,其实调用方法跟上面的一致
self.postMessage({
type: 'addFileOrIns',
data: {
res: 'error',
type: 'addFileOrIns',
id: newId,
timestamp: new Date().toISOString(),
}
})这里的self就是对应上面this.worke
-
这里有个问题需要注意一下,这里每次通信传递的数据类型,就是data里面带的数据,不能是复杂数据类型的,如果想传复杂数据类型的话可以通过两种形式来传
- 第一种就是把复杂类型转化成字符串进行传递,JSON.stringify(newItem)这种写法,这种写法方便快捷,但是有个问题就是当数据量过大时传输效率太低了,适合不是很复杂的数据类型
- 第二种就是通过把数据类型先转换成字符串,在转化成二进制数据流进行传递,这样操作的比较麻烦,但是数据传输速度上面要快很多
-
然后我就可以在class中和data-worke中定义一一对应的消息通知,只有触发到对应的消息通知,才会调用对应的方法
-
data-worker文件做为初始化的时候调用的文件,其实可以类似于一个入口文件,当他的业务复杂的时候可以把业务抽离成单独的文件,然后在data-worker中进行调用
此处放下全部代码
DataMonitor.ts
// 数据监控类,封装所有监控相关的方法
// 监控配置类型
interface MonitorConfig {
interval: number | string;
dataSource: string;
apiUrl?: string | null;
threshold: number | string;
}
// 状态变化数据类型
interface StatusChangeData {
isMonitoring?: boolean;
isPaused?: boolean;
startTime?: Date;
}
// Worker消息事件类型
interface WorkerMessageEvent {
data: {
type: string;
data?: any;
timestamp?: number;
};
}
// 回调函数选项类型
interface DataMonitorOptions {
config: MonitorConfig;
onLog: (message: string, level: string, timestamp?: number) => void;
onStatusChange: (status: StatusChangeData) => void;
onDataUpdate: (data: any) => void;
onUptimeUpdate: (uptime: string) => void;
}
export class DataMonitor {
private options: DataMonitorOptions;
private worker: Worker | null;
constructor(options: DataMonitorOptions) {
this.options = options;
this.worker = null;
}
// 初始化Web Worker
initWorker() {
try {
this.worker = new Worker(new URL('./data-worker.ts', import.meta.url), { type: 'module' });
// 监听Worker消息
this.worker.onmessage = this.handleWorkerMessage.bind(this);
this.worker.onerror = this.handleWorkerError.bind(this);
this.options.onLog('Worker初始化成功', 'success');
} catch (error: unknown) {
this.options.onLog(`Worker初始化失败: ${error instanceof Error ? error.message : String(error)}`, 'error');
}
}
// 处理Worker消息
handleWorkerMessage(event: WorkerMessageEvent) {
const { type, data, timestamp } = event.data;
switch (type) {
case 'status':
this.updateStatus(data);
break;
case 'data_error':
this.handleDataError(data);
break;
case 'check_result':
this.handleCheckResult(data);
break;
case 'log':
this.options.onLog(data.message, data.level, timestamp);
break;
default:
this.options.onLog(`未知消息类型: ${type}`, 'warning');
}
}
// 处理Worker错误
handleWorkerError(error: ErrorEvent) {
this.options.onLog(`Worker错误: ${error.message}`, 'error');
this.stopMonitoring();
}
// 开始监控
startMonitoring() {
if (!this.worker) {
this.initWorker();
}
if (this.worker) {
this.worker.postMessage({
type: 'start',
});
}
this.options.onLog(`开始监控 - 间隔: ${this.options.config.interval}ms, 数据源: ${this.options.config.dataSource}`, 'info');
}
// 停止监控
stopMonitoring() {
if (this.worker) {
this.worker.postMessage({ type: 'stop' });
}
this.options.onLog('监控已停止', 'info');
}
// 立即检查一次
checkNow() {
if (this.worker) {
this.worker.postMessage({
type: 'check_once',
});
this.options.onLog('执行单次检查', 'info');
}
}
// 处理数据错误
handleDataError(data: any) {
this.options.onLog(`数据错误: ${data.error} (源: ${data.source})`, 'error');
this.handleError(data);
}
// 处理检查结果
handleCheckResult(data: any) {
//根据返回的结果去更新对应的数据
this.updateStatus(data)
}
// 更新状态信息
updateStatus(data: any) {
this.options.onLog(`更新状态: ${JSON.stringify(data).substring(0, 100)}...`, 'status');
}
// 处理错误
handleError(data: any) {
this.options.onLog(`执行错误处理: ${data.error}`, 'error');
}
}
data-worker.ts
/**
* 数据监控Worker - 后台定时检查线程
*/
export interface Config {
interval: number | string;
dataSource: string;
apiUrl?: string | null;
threshold: number | string;
}
let monitoringInterval: number | null = null;
let isActive = false;
let checkCount = 0;
// 监听主线程消息
self.onmessage = function (event) {
const { type, config: newConfig } = event.data;
switch (type) {
case 'start':
startMonitoring(newConfig);
break;
case 'pause':
pauseMonitoring();
break;
case 'stop':
stopMonitoring();
break;
case 'check_once':
// 执行单次检查
break
default:
sendLog(`未知命令: ${type}`, 'warning');
}
};
// 开始监控
function startMonitoring(newConfig: Config) {
// 发送检查结果
self.postMessage({
type: 'check_result',
data: {
backData: '111',
timestamp: new Date().toISOString(),
}
});
}
// 暂停监控
function pauseMonitoring() {
if (!isActive) {
sendLog('监控未运行,无法暂停', 'warning');
return;
}
isActive = false;
sendStatus('监控已暂停');
sendLog('监控已暂停', 'warning');
}
// 停止监控
function stopMonitoring() {
isActive = false;
if (monitoringInterval) {
clearInterval(monitoringInterval);
monitoringInterval = null;
}
sendStatus('监控已停止');
sendLog(`监控已停止,共执行 ${checkCount} 次检查`, 'info');
}
// 执行数据检查
// 获取数据(根据不同的数据源)
async function fetchData(currentConfig) {
const { dataSource, apiUrl } = currentConfig;
switch (dataSource) {
case 'mockApi':
return fetchMockData();
case 'localStorage':
return fetchLocalStorageData();
case 'websocket':
return fetchWebSocketData();
case 'externalApi':
if (!apiUrl) {
throw new Error('API地址未配置');
}
return fetchExternalApiData(apiUrl);
default:
throw new Error(`不支持的数据源: ${dataSource}`);
}
}
// 模拟API数据
async function fetchMockData() {
// 模拟API延迟
await sleep(Math.random() * 1000 + 500);
// 随机决定是否有数据
if (Math.random() > 0.3) { // 70%的概率有数据
return {
timestamp: new Date().toISOString(),
data: {
value: Math.floor(Math.random() * 100),
items: Array.from({ length: Math.floor(Math.random() * 5) + 1 }, (_, i) => ({
id: i + 1,
name: `项目${i + 1}`,
status: Math.random() > 0.5 ? 'active' : 'inactive'
})),
metadata: {
source: 'mock_api',
version: '1.0'
}
}
};
} else {
// 30%的概率返回空数据
return null;
}
}
// 从localStorage获取数据
async function fetchLocalStorageData() {
await sleep(100); // 模拟微小延迟
const data = localStorage.getItem('monitor_test_key');
if (data) {
return {
timestamp: new Date().toISOString(),
data: JSON.parse(data),
source: 'localStorage'
};
}
return null;
}
// WebSocket数据(模拟)
async function fetchWebSocketData() {
await sleep(Math.random() * 800 + 200);
// 模拟WebSocket数据
const hasData = Math.random() > 0.4;
if (hasData) {
return {
timestamp: new Date().toISOString(),
data: {
type: 'ws_update',
payload: {
users: Math.floor(Math.random() * 1000),
messages: Math.floor(Math.random() * 5000),
connections: Math.floor(Math.random() * 100)
}
},
source: 'websocket'
};
}
return null;
}
// 获取外部API数据
async function fetchExternalApiData(apiUrl: string) {
// 注意:实际使用中可能会遇到CORS问题
// 这里添加了模拟实现
await sleep(Math.random() * 1500 + 500);
try {
// 如果是演示,可以模拟响应
if (apiUrl.includes('example.com')) {
// 模拟API响应
return {
timestamp: new Date().toISOString(),
data: {
success: true,
value: Math.floor(Math.random() * 100),
message: '数据获取成功'
},
source: 'external_api'
};
} else {
// 尝试真实请求(注意CORS)
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`API请求失败: ${response.status}`);
}
const data = await response.json();
return {
timestamp: new Date().toISOString(),
data: data,
source: 'external_api',
rawResponse: response
};
}
} catch (error) {
// 如果真实请求失败,返回模拟数据作为fallback
sendLog(`外部API请求失败: ${error.message},使用模拟数据`, 'warning');
return {
timestamp: new Date().toISOString(),
data: {
success: false,
fallback: true,
value: Math.floor(Math.random() * 100),
message: '这是回退数据'
},
source: 'external_api_fallback'
};
}
}
// 检查数据是否为空
function isDataEmpty(data) {
if (!data) return true;
// 检查数据对象是否为空
if (typeof data === 'object') {
if (Array.isArray(data) && data.length === 0) return true;
if (Object.keys(data).length === 0) return true;
// 检查嵌套的数据字段
if (data.data === null || data.data === undefined) return true;
if (typeof data.data === 'object' && Object.keys(data.data).length === 0) return true;
}
return false;
}
// 处理找到的数据
function handleDataFound(data, config) {
sendLog(`发现有效数据 (检查点: ${checkCount})`, 'success');
// 处理数据验证
const validatedData = validateData(data, config);
self.postMessage({
type: 'data_found',
data: validatedData,
timestamp: new Date().toISOString()
});
}
// 处理空数据
function handleEmptyData(data, config) {
self.postMessage({
type: 'data_empty',
data: {
checkpoint: checkCount,
timestamp: new Date().toISOString(),
reason: '数据为空或无内容',
config: config.dataSource
},
timestamp: new Date().toISOString()
});
}
// 处理数据错误
function handleDataError(error, config) {
self.postMessage({
type: 'data_error',
data: {
error: error.message,
source: config.dataSource,
checkpoint: checkCount,
timestamp: new Date().toISOString()
},
timestamp: new Date().toISOString()
});
}
// 验证数据
function validateData(data, config) {
const validated = { ...data };
// 添加验证标记
validated.validated = true;
validated.validationTime = new Date().toISOString();
// 简单的数据清洗
if (validated.data && validated.data.value !== undefined) {
// 确保数值在合理范围内
validated.data.value = Math.max(0, Math.min(100, validated.data.value));
}
// 添加检查计数
validated.checkCount = checkCount;
return validated;
}
// 发送状态更新
function sendStatus(message) {
self.postMessage({
type: 'status',
data: {
message,
isActive,
checkCount,
timestamp: new Date().toISOString()
}
});
}
// 发送日志
function sendLog(message, level = 'info') {
self.postMessage({
type: 'log',
data: {
message,
level,
timestamp: new Date().toISOString()
}
});
}
// 工具函数:延迟
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Worker初始化完成
sendLog('数据监控Worker已加载', 'success');
sendStatus('准备就绪');
// 错误处理
self.onerror = function (error) {
sendLog(`Worker发生错误: ${error.message}`, 'error');
};
总结
个人感觉使用webworker可以把业务代码抽离出来,然后通过通信的方式去进行调用,而且此时的业务代码个个是独立的,他们直接互不影响,但是可以通过通信来进行互相调用,而且它不堵塞主线程,这点用着很舒服。