使用Web Worker的经历

什么是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模块

进行通信

  1. 通信有两种通信情况
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可以把业务代码抽离出来,然后通过通信的方式去进行调用,而且此时的业务代码个个是独立的,他们直接互不影响,但是可以通过通信来进行互相调用,而且它不堵塞主线程,这点用着很舒服。
相关推荐
发现一只大呆瓜2 小时前
JS-类型转换:从显式“强制”到隐式“魔法”
javascript
!执行2 小时前
高德地图 JS API 在 Linux 系统的兼容性解决方案
linux·前端·javascript
Gooooo2 小时前
现代浏览器的工作原理
前端
发现一只大呆瓜2 小时前
JS-ES6新特性
javascript
kk晏然2 小时前
TypeScript 错误类型检查,前端ts错误指南
前端·react native·typescript·react
纆兰2 小时前
汇款单的完成
前端·javascript·html
Lsx_3 小时前
案例+图解带你遨游 Canvas 2D绘图 Fabric.js🔥🔥(5W+字)
前端·javascript·canvas
2501_944521003 小时前
rn_for_openharmony商城项目app实战-主题设置实现
javascript·数据库·react native·react.js·ecmascript
m0_471199633 小时前
【场景】如何快速接手一个前端项目
前端·vue.js·react.js