0
点赞
收藏
分享

微信扫一扫

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

小云晓云 2022-02-15 阅读 43

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;
};

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

// 调用接口
const query = async (
  isShared: boolean,
  type: ComponentType,
  source: VISUAL_TYPE,
  resourceId: string | number,
  params,
  sharedComponentQueryResult,
  horusParams,
  skipCancel: boolean = false,
  // eslint-disable-next-line max-params
) => {
  let resultData;
  await axios.post(`******`, params, {
        headers: {},
        cancelToken: cancelToken.token,
      })
      .then(res => {
        if (res.status === 200 && res.data.code === 0) {
          // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
          resultData = res.data.data;
        } else if (res.status === 404) {
          // eslint-disable-next-line no-throw-literal
          throw 'request 404';
        } else {
          // eslint-disable-next-line no-throw-literal
          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;
        console.log(message, 'message');
        const logId = err.logId ?? '';
        // eslint-disable-next-line no-throw-literal
        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>(
    isShared: boolean,
    source: VISUAL_TYPE,
    resourceId: string,
    type: ComponentType,
    params: any,
    sharedComponentQueryResult: any,
    skipCancel: boolean = false,
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果未初始化,初始化
      this.init();
      query(isShared, type, source, resourceId, params, sharedComponentQueryResult,  skipCancel)
        .then(data => {
          if (
            type !== ComponentType.RICH_TEXT
            && type !== ComponentType.TABLE
            && type !== ComponentType.COCKPIT_RICH_TEXT
          ) {
           // 查询到数据之后给worker发送消息启动格式化数据线程
            this._worker?.postMessage({
              method: 'format',
              options: {
                id,
                data,
                type,
                params,
                lang: getCurrentLang()
              },
            });
          } else {
            this.messageHandler(id, data);
          }
        })
        .catch((e: Error) => {
          this.messageHandler(id, null, e);
        });
    });
  }

  /**
   *
   * @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 {
  isShared: boolean;
  source: VISUAL_TYPE;
  resourceId: string;
  type: ComponentType;
  id: number;
  params: any;
  sharedComponentQueryResult: any;
  baseUrl: string;
  horusParams: 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;
    }
    switch (type) {
      case ComponentType.MUTI_METRIC_CARD:
        result = transformMultiMetricCard(data, params, lang);
        break;
      case ComponentType.SINGLE_METRIC_CARD:
        result = transformSingleMetricCard(data, params, lang);
        break;
      case ComponentType.PROCESS:
        result = formatProgress(data, params);
        break;
      case ComponentType.LINE:
        result = formatLine(data, params);
        break;
      case ComponentType.PIE:
        result = formatPie(data, params);
        break;
      case ComponentType.BAR:
        result = formatBar(data, params);
        break;
      case ComponentType.BAR_ROW:
        result = formatBarRow(data, params);
        break;
      case ComponentType.TABLE:
      case ComponentType.COCKPIT_RICH_TEXT:
      case ComponentType.RICH_TEXT:
      default:
        break;
    }
  } 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 (isShared, type, resourceId, params, sharedComponentQueryResult) => {
      isLoading.value = true;
      const { data, series, delayedMessage, feQueryConfig, percent: percentConfig } = await WorkerHelper.query(
        isShared,
        type,
        resourceId,
        ComponentType.BAR,
        params,
        sharedComponentQueryResult
      );
      ...
    };
...
举报

相关推荐

0 条评论