记一次面试题:多频次处理但不影响主页面

76 阅读2分钟

题目(大概意思):开发一个浏览器前端应用,将高频次的请求某个方法对数据进行解析(假设方法,不用你写),如何做到不阻塞主页面的运行,不影响主页面的性能。 ps: 该方法返回一个promise,并可以在then中输入解析结果

分析

不影响页面,那必定webworker

避免高频次,那必定加延时,总所周知,setTimeout是不阻塞主进程的

难就难在怎么在worker的消息回调中返回promise的reslove

个人实现(仅供参考)

预览链接(需打开控制台): 切图喵-webworker-切图喵 (qtcat.cn)

主页面,main.vue

<script lang="ts">
export default {
  meta: {
    path: '/webworker',
    title: 'webworker',
    enTitle: 'webworker',
    auth: false,
    cache: false,
  },
  name: 'webworker',
};
</script>
<script lang="ts" setup>
import { onUnmounted, ref } from 'vue';
import _ from 'lodash';
const taskNum = ref(10);
class ParseWorker {
  queue: Array<any>;
  worker: Worker | null;
  maxQueue: number;
  waitNum: number;
  constructor(maxQueue = 1) {
    this.maxQueue = maxQueue; // 当前队列多于多少个时,延时执行
    this.queue = [];
    this.waitNum = 0; //等候的数量
    this.worker = null;
  }
  public start() {
    const workerPath: URL = new URL(
      `${location.origin}/source/js/webworker.js`
    );
    this.worker = new Worker(workerPath);
    this.worker.onmessage = (evt) => {
      const { data } = evt;
      this.queue.forEach((q, index) => {
        if (q.id === data.id) {
          q.resolve(data);
          this.queue.splice(index, 1); //队列中删除该项
        }
      });
    };
  }
  protected postWorker(res, resolve, reject) {
    const id = _.uniqueId();
    this.worker?.postMessage({
      type: 'parse',
      id,
      start: Date.now(),
      data: res,
    });
    this.queue.push({
      id,
      resolve,
      reject,
    });
  }
  public parse(res: any) {
    return new Promise((resolve, reject) => {
      if (!this.worker) {
        reject();
        return;
      }
      if (this.queue.length >= this.maxQueue) {
        this.waitNum++;
        setTimeout(() => {
          this.postWorker(res, resolve, reject);
          this.waitNum--;
        }, 100 * this.waitNum);
      } else {
        this.postWorker(res, resolve, reject);
      }
    });
  }
  public destroyWorker() {
    console.warn('this.worker terminate and queue cleared');
    this.queue = [];
    this.waitNum = 0;
    this.worker?.terminate();
  }
}
const parser = new ParseWorker(2);

onUnmounted(() => {
  parser.destroyWorker();
});
function stop() {
  parser.destroyWorker();
}
function start() {
  parser.start();
  const arr: Array<number | string> = [];
  for (let i = 0; i < taskNum.value; i++) {
    arr.push(Math.random() * 1100); //模拟的数据
  }
  arr.forEach((item) => {
    parser.parse(item).then((res) => {
      console.warn('parse res', res);
    });
  });
}
</script>

<template>
  <ck-page class="page" footer header>
    <p>
      题目(大概意思):开发一个浏览器前端应用,将高频次的请求某个方法对数据进行解析(假设方法,不用你写),如何做到不阻塞主页面的运行,不影响主页面的性能。
      ps: 该方法返回一个promise,并可以在then中输入解析结果
    </p>
    <br />
    <el-form @submit.prevent>
      <el-form-item label="任务数">
        <el-input v-model="taskNum" type="number">输入任务数</el-input>
      </el-form-item>
    </el-form>
    <el-button type="primary" @click="start"
      >开始解析(输出请f12看控制台)</el-button
    >
    <el-button type="danger" @click="stop">停止解析</el-button>
  </ck-page>
</template>
<style>
.page {
  padding: 1em;
}
</style>

webworker.js

console.warn('worker  start');
onmessage = (evt) => {
  const { data } = evt;
  console.log('message from page', data);
  setTimeout(
    () => postMessage({ ...data, time: Date.now() }),
    1000 + 2000 * Math.random()
  );
};