高量级任务执行优化
宏任务的高并发
在一个页面中,如果有大量的任务需要执行,那么就会造成页面的卡顿,这时候就需要对任务进行优化,让任务分散到不同的时间段执行,这样就不会造成页面的卡顿。
首先是页面的渲染逻辑:也就是事件循环, !事件循环 所以在整个的优化区间是在宏任务中,微任务会在整个微队列清空之后,才会执行下一个宏任务。所以在宏任务中,我们可以将任务分散到不同的时间段执行,这样就不会造成页面的卡顿。
- 方案一:worker 示例demo
// worker是一个独立的线程,可以将一些耗时的任务放到worker中执行,这样就不会阻塞主线程,从而提高页面的性能
// worker中不能操作DOM,所以需要将数据传递给主线程,然后再由主线程操作DOM
// worker中的代码必须是独立的文件,不能在当前文件中写
// worker中的代码必须是纯净的,不能操作DOM,也不能操作window对象 和 document对象(self对象除外)
// worker.js
self.addEventListener('message', function(e) {
if (e.data === 'start') {
// 执行耗时任务
let start = Date.now();
let result = performHeavyTask();
let end = Date.now();
self.postMessage('耗时任务执行完毕,结果为:' + result + ',耗时:' + (end - start) + 'ms');
}
});
function performHeavyTask() {
// 这里是耗时任务的代码
// 例如,进行复杂计算或处理大量数据
let sum = 0;
for (let i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
}
// main
// 主线程中的代码
document.getElementById('startButton').addEventListener('click', function() {
if (window.Worker) {
const myWorker = new Worker('worker.js');
myWorker.postMessage('start');
myWorker.onmessage = function(e) {
console.log('任务完成:', e.data);
};
} else {
console.log('您的浏览器不支持 Web Workers。');
}
});
- 方案二:requestAnimationFrame requestAnimationFrame是浏览器提供的一个API,可以将任务分散到不同的时间段执行,从而提高页面的性能 requestAnimationFrame的回调函数会在浏览器下一次重绘之前执行,所以可以将任务分散到不同的时间段执行 requestAnimationFrame的回调函数中的this指向window对象 requestAnimationFrame的回调函数中的参数是一个时间戳,表示requestAnimationFrame开始去执行回调函数的时间
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>requestAnimationFrame 任务优化</title>
<style>
.spinner {
width: 40px;
height: 40px;
background-color: #333;
margin: 100px auto;
animation: spin 1s infinite linear;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="spinner"></div>
<button id="startButton">执行耗时任务</button>
<script>
document.getElementById('startButton').addEventListener('click', function() {
let count = 0;
const maxCount = 1000000; // 假设任务是计数到一百万
function performTask() {
const start = performance.now();
while (performance.now() - start < 16) {
if (count >= maxCount) {
console.log('任务完成');
return;
}
count++;
}
requestAnimationFrame(performTask);
}
requestAnimationFrame(performTask);
});
</script>
</body>
</html>
- 方案三:requestIdleCallback
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>requestIdleCallback 任务优化</title>
<style>
.spinner {
width: 40px;
height: 40px;
background-color: #333;
margin: 100px auto;
animation: spin 1s infinite linear;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="spinner"></div>
<button id="startButton">执行耗时任务</button>
<script>
document.getElementById('startButton').addEventListener('click', function() {
let count = 0;
const maxCount = 1000000; // 假设任务是计数到一百万
function performIdleTask(deadline) {
while (count < maxCount && deadline.timeRemaining() > 0) {
count++;
}
if (count < maxCount) {
requestIdleCallback(performIdleTask);
} else {
console.log('任务完成');
}
}
requestIdleCallback(performIdleTask);
});
</script>
</body>
</html>
- 方案四:Generators
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Generator 任务优化</title>
<style>
.spinner {
width: 40px;
height: 40px;
background-color: #333;
margin: 100px auto;
animation: spin 1s infinite linear;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="spinner"></div>
<button id="startButton">执行耗时任务</button>
<script>
function* taskGenerator(maxCount) {
let count = 0;
while (count < maxCount) {
yield count++;
}
}
document.getElementById('startButton').addEventListener('click', function() {
const maxCount = 1000000; // 假设任务是计数到一百万
const gen = taskGenerator(maxCount);
function runTask() {
const result = gen.next();
if (!result.done) {
setTimeout(runTask, 0);
} else {
console.log('任务完成');
}
}
runTask();
});
</script>
</body>
</html>
微任务的高并发
对于微任务的高并发,重要的便是不同任务的执行时间不同,将api的调用和返回结果一一对应是最好的,这样就不会造成页面的卡顿。
function createAsyncTaskQueue() {
let tasks = [];
let isTaskRunning = false;
function enqueue(task) {
tasks.push(task);
if (!isTaskRunning) {
isTaskRunning = true;
runNextTask();
}
}
function runNextTask() {
if (tasks.length === 0) {
isTaskRunning = false;
return;
}
const task = tasks.shift();
task().then(() => {
runNextTask();
});
}
return { enqueue };
}
// vue中
<template>
<div>
<h1>{{ message }}</h1>
<button @click="testQueue">Test Async Queue</button>
</div>
</template>
<script >
import { ref } from 'vue';
export default {
setup() {
const message = ref('Ready to test async queue');
const asyncQueue = createAsyncTaskQueue();
const testQueue = () => {
message.value = 'Starting async tasks...';
asyncQueue.enqueue(() => delayTask(1000, 'Task 1 completed'));
asyncQueue.enqueue(() => delayTask(2000, 'Task 2 completed'));
asyncQueue.enqueue(() => delayTask(1500, 'Task 3 completed'));
};
function delayTask(delay, result) {
return new Promise(resolve => {
setTimeout(() => {
message.value = result;
resolve();
}, delay);
});
}
return {
message,
testQueue
};
}
}
</script>
<style scoped>
h1 {
color: #42b983;
}
</style>
这种方式,通过将api的调用和执行进行绑定,但是这样回丢失掉并发,得不偿失。也可以通过生成对应的时间戳等等,但是也可以直接将then一起传过来,来解决不一致的问题,同时也可以通过控制请求的数量来控制并发的数量。
function createAsyncTaskQueue(maxConcurrency) {
let tasks = [];
let currentRunning = 0;
let taskCounter = 0;
function enqueue(task) {
const taskId = taskCounter++;
return new Promise((resolve, reject) => {
tasks.push({ task, resolve, reject, taskId });
processTasks();
});
}
function processTasks() {
while (currentRunning < maxConcurrency && tasks.length > 0) {
currentRunning++;
const { task, resolve, reject, taskId } = tasks.shift();
task().then(result => {
resolve(result);
currentRunning--;
processTasks();
}).catch(error => {
reject(error);
currentRunning--;
processTasks();
});
}
}
return { enqueue };
}
// 使用队列,最多同时运行3个异步任务
const asyncQueue = createAsyncTaskQueue(3);
// vue
<template>
<div>
<button @click="testQueue">Test Async Queue</button>
<p v-for="(result, index) in results" :key="index">{{ result }}</p>
</div>
</template>
<script>
import { ref } from 'vue';
function createAsyncTaskQueue(maxConcurrency) {
let tasks = [];
let currentRunning = 0;
let taskCounter = 0;
function enqueue(task) {
const taskId = taskCounter++;
return new Promise((resolve, reject) => {
tasks.push({ task, resolve, reject, taskId });
processTasks();
});
}
function processTasks() {
while (currentRunning < maxConcurrency && tasks.length > 0) {
currentRunning++;
const { task, resolve, reject, taskId } = tasks.shift();
task().then(result => {
resolve(result);
currentRunning--;
processTasks();
}).catch(error => {
reject(error);
currentRunning--;
processTasks();
});
}
}
return { enqueue };
}
export default {
setup() {
const results = ref([]);
const asyncQueue = createAsyncTaskQueue(3);
const testQueue = () => {
results.value = []; // 清空之前的结果
asyncQueue.enqueue(() => delayTask(1000, 'Task 1 completed').then((res) => { console.log(res) }));
asyncQueue.enqueue(() => delayTask(2000, 'Task 2 completed').then((res) => { console.log(res) }));
asyncQueue.enqueue(() => delayTask(1500, 'Task 3 completed').then((res) => { console.log(res) }));
};
function delayTask(delay, result) {
return new Promise(resolve => {
setTimeout(() => {
// results.value.push(result); // 将结果添加到数组
resolve(result);
}, delay);
});
}
return {
results,
testQueue
};
}
}
</script>
<style scoped>
/* 可以在这里添加样式 */
</style>