题目(大概意思):开发一个浏览器前端应用,将高频次的请求某个方法对数据进行解析(假设方法,不用你写),如何做到不阻塞主页面的运行,不影响主页面的性能。 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()
);
};