欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端项目中使用Web Worker的具体实现

程序员文章站 2022-03-01 12:39:38
...

1 实际项目中使用Web Worker

下面这篇文章详细介绍了web worker常用的api, 场景(这里不知道有没有具体衡量指标),使用过程中的注意事项,文章写的很好,感兴趣可以看下,这里不在详细叙述具体概念
Web Worker使用

1.1 场景

这里分享的一个场景,是把从数据查询接口获取的数据的格式化过程放在web worker中执行。

首先简单介绍下我们的项目,一个可视化相关项目,接口主要分为两大方面,配置查询和数据查询接口,其中数据查询接口是影响页面性能指标的一个很关键的因素,并且从查询接口获取到数据之后的格式化过程逻辑全部放在fe中,数据格式化这个过程可能涉及一些比较大的计算,因此我们选择将它拆出来放在一个单独web worker线程中,减少对主线程造成影响。

1.2 实现

具体解释看注释

// workerHelper.ts
import Worker from 'worker-loader?inline=no-fallback!./query.worker.ts';
import axios, { CancelTokenSource } from 'axios';
import { ErrorType } from '@/api';
const { CancelToken } = axios;

type EventHanler = {
  resolve: (value: any) => void;
  reject: (reason?: any) => void;
};
export enum ErrorType {
  TIMEOUT,
  REQUEST,
  CANCEL
}

const requestMap = new Map<string, CancelTokenSource>();

// 调用接口
const query = async (
  params,
) => {
  let resultData;
  await axios.post(`******`, params, {
        headers: {},
        cancelToken: cancelToken.token,
      })
      .then(res => {
        if (res.status === 200 && res.data.code === 0) {
          resultData = res.data.data;
        } else if (res.status === 404) {
          throw 'request 404';
        } else {
          throw {
            message: res.data.msg || res.statusText || '',
            logId,
          };
        }
      })
      .catch(err => {
        // 不是手动取消
        const errType = err.__CANCEL__
          ? ErrorType.CANCEL
          : (err.message ?? err)?.includes('timeout')
            ? ErrorType.TIMEOUT
            : ErrorType.REQUEST;
        const message = errType === ErrorType.TIMEOUT ? 'request timeout' : err.message ?? err;
        const logId = err.logId ?? '';
        throw {
          message,
          type: errType,
          logId,
        };
      });
  return resultData;
};

export default class WorkerHelper {
  private static _worker: Worker | null;  // 定义一个worker
  // 初始化
  static init() {
    if (!this._worker) {
      // 注册一个数据格式化worker
      this._worker = new Worker();
      // webworker异常
      this._worker.addEventListener('error', e => {
        throw e;
      });
    }
  }

  // 查询函数,对外提供
  static query<T = any>(
    params: any,
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果未初始化,初始化
      this.init();
      query(params)
        .then(data => {
           // 查询到数据之后给worker发送消息启动格式化数据线程
            this._worker?.postMessage({
              method: 'format',
              options: {
                id,
                data,
                type,
                params,
                lang: getCurrentLang()
              },
            });
        })
        .catch((e: Error) => {
        });
    });
  }

  /**
   *
   * @param id 组件id
   */
  static cancelQuery(id: string) {
    requestMap.get(id as string)?.cancel();
  }

  /**
   *
   * @param 批量取消
   */
  static cancelMoreQuery(ids: string[]) {
    for(let id of ids){
      requestMap.get(id as string)?.cancel();
    }
    
  }
  // 终止worker
  static terminate() {
    this._worker?.terminate();
    this._worker = null;
  }
}
// 格式化worker
// query.worker.ts
interface QueryParams {
  params: any;
}

interface CancelParams {
  id: string;
}
const ctx: Worker = self as any;
const formatHandler = async options => {
  const { data, id, type, params, lang } = options;
  let result, error;
  try {
    if (!id) {
      error = new Error('id 不存在');
      throw error;
    }
    ...
    result = forMate(data)
  } catch (e) {
    error = e;
  } finally {
    ctx.postMessage({ id, data: result, error });
  }
};
ctx.addEventListener(
  'message',
  (
    event: MessageEvent<{
      method: 'format' | 'cancelQuery';
      options: QueryParams | CancelParams;
    }>
  ) => {
    const {
      data: { method, options },
    } = event;
    if (method === 'format') {
      // 格式化数据
      formatHandler(options);
    }
  }
);
// 具体使用
import WorkerHelper from '@/utils/workerHelper';
...
 const queryData = async (params) => {
      isLoading.value = true;
      const { data, series, delayedMessage, feQueryConfig, percent: percentConfig } = await WorkerHelper.query(
        params,
      );
      ...
    };
...