Demo 地址
先上 demo 地址
https://codepen.io/firstblood…
我的项目背景
随着 面向领导编程
越来越深入人心, 看板
我的项目想必是每个前端开发专家的必修之路
产品经理 张三
要求 前端开发专家 红盾
做一个由 1000 个图表定时刷新
的公司最新的财务收入状况
红盾
在 chrome
上 一顿操作猛如虎, 没两三天就把 我的项目搞定了 交付给产品经理 张三
.
无并发管制
让咱们来看看 红盾
此时大抵的代码状况
<template>
<div class="app-container">
<div class="charts">
<div v-for="item in domList" :id="item" :key="item" class="chart" />
</div>
</div>
</template>
<script>
const echarts = require("echarts");
const chartNum = 1000; // 图表数量
const chartIntervalTime = 2000; // 图表定时渲染毫秒数
export default {data() {
return {domList: [],
chartObjs: {},
chartData: [150, 230, 224, 218, 135, 147, 260],
};
},
mounted() {
// 创立 echart 并绘图
this.createChart();
// 隔 3 秒更新图表数据并渲染
this.intervalChartData(chartIntervalTime);
},
methods: {
// 创立 echart 并绘图
async createChart() {for (let i = 1; i <= chartNum; i++) {this.domList.push("chart" + i);
}
this.$nextTick(this.renderChartList);
},
async renderChartList() {this.domList.forEach((dom) => this.initChart(dom));
},
// 隔 3 秒更新图表数据并渲染
intervalChartData(s) {setInterval(() => {this.renderChartList();
}, s);
},
// 初始化图表
initChart(domId) {if (!this.chartObjs[domId]) {this.chartObjs[domId] = echarts.init(document.getElementById(domId));
}
const option = {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {type: "value",},
series: [
{
data: this.chartData,
type: "line",
},
],
};
this.chartObjs[domId].clear();
this.chartObjs[domId].setOption(option);
},
},
};
</script>
<style scoped>
.chart {
float: left;
width: 360px;
height: 300px;
margin: 10px;
border: 2px solid #ff9900;
}
</style>
后果 张三
不按套路出牌, 在电视机上偷偷安了个 浏览器(性能极差), 而后输出地址 xxx.com
(自行脑补)…
间接把浏览器给淦奔溃了 … 没错, 红盾
此时的心田
这时候 他想起了 后端大佬 老黄
, 老黄给了他一个思路 管制并发
实现管制并发
实现并发
- 首先咱们来实现一个管制
并发函数
想间接进入主题的同学能够间接跳到 残缺代码
查看
/**
* @params {Number} poolLimit - 最大并发限度数
* @params {Array} array - 所有的并发申请 | 渲染数组
* @params {Function} iteratorFn - 对应执行的并发函数(承受 array 的每一项值)
*/
async function asyncPool(poolLimit, array, iteratorFn) {const ret = [] // 所有执行中的 promises
let executing = [] // 正在执行中的 promises
for (const item of array) {
// 承受 iteratorFn 的返回值:Promise
const p = Promise.resolve().then(() => iteratorFn(item))
ret.push(p)
// 如果执行的数组 大于等于 最大并发限度 那么咱们就要管制并发
if (array.length >= poolLimit) {const e = p.then(() => executing.splice(executing.indexOf(e), 1))
// p.then 返回的 一个 Promise 咱们把它放到正在执行数组中, 一旦执行完 便剔除对应的值
executing.push(e)
// 外围代码: 正在执行的 promises 数组 大于等于 ` 最大并发限度 ` 用.race 办法开释一个执行最快的
if (executing.length >= poolLimit) await Promise.race(executing)
}
}
// 返回一个 Promise.all
return Promise.all(ret)
}
-
革新
renderChartList
函数 (外围
)async renderChartList() { // 这里的 MAX_CURRENT 之后能够自定义一个数字 await asyncPool(MAX_CURRENT, this.domList, (dom) => { // 咱们在这里必须返回一个 promise 对应为 ` 并发函数 ` 的 `p` 变量 return new Promise(async (resolve) => {const res = await this.initChart(dom); resolve(res);// 这一步之后, 对应执行 ` 并发函数 ` 的 p.then 剔除 }).then((data) => {console.log(data); return data; }); }); }
3. 革新 initChart
函数
咱们必须保障一个图表渲染实现, 再执行下一个渲染, 此时咱们就须要监听 Echarts
的 finished
事件
initChart(domId) {
// 咱们 把它革新成一个 promise 函数
return new Promise((resolve) => {
...
// 外围代码 监听 echarts 的 finished
this.chartObjs[domId].on("finished", () => {resolve(domId);// 对应 上一步的 `const res = await this.initChart(dom);`
});
});
}
4. 革新 intervalChartData
函数
咱们必须保障并发执行完 所有的图表渲染, 再进入下一个定时器逻辑
判断 executing
的长度即可(此时应该把 executing
独立为全局变量)
intervalChartData(s) {setInterval(() => {if (executing.length > 0) return; // 还有正在执行的渲染 不反复增加
this.renderChartList();}, s);
}
残缺代码
附上残缺代码
<template>
<div class="app-container">
<div class="charts">
<div v-for="item in domList" :id="item" :key="item" class="chart" />
</div>
</div>
</template>
<script>
const echarts = require("echarts");
const chartNum = 1000; // 图表数量
const MAX_CURRENT = 50; // 图表最大渲染并发数
const chartIntervalTime = 2000; // 图表定时渲染毫秒数
let executing = [];
/**
* @params {Number} poolLimit - 最大并发限度数
* @params {Array} array - 所有的并发申请 | 渲染数组
* @params {Function} iteratorFn - 对应执行的并发函数(承受 array 的每一项值)
*/
async function asyncPool(poolLimit, array, iteratorFn) {const ret = []; // 所有执行中的 promises
executing = []; // 正在执行中的 promises
for (const item of array) {const p = Promise.resolve().then(() => iteratorFn(item));
ret.push(p);
if (array.length >= poolLimit) {const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) await Promise.race(executing);
}
}
return Promise.all(ret);
}
export default {data() {
return {domList: [],
chartObjs: {},
chartData: [150, 230, 224, 218, 135, 147, 260],
};
},
mounted() {
// 创立 echart 并绘图
this.createChart();
// 隔 3 秒更新图表数据并渲染
this.intervalChartData(chartIntervalTime);
},
methods: {
// 创立 echart 并绘图
async createChart() {for (let i = 1; i <= chartNum; i++) {this.domList.push("chart" + i);
}
this.$nextTick(this.renderChartList);
},
async renderChartList() {const res = await asyncPool(MAX_CURRENT, this.domList, (i, arr) => {return new Promise(async (resolve) => {const res = await this.initChart(i);
resolve(res);
}).then((data) => {console.log(data);
return data;
});
});
},
// 隔 3 秒更新图表数据并渲染
intervalChartData(s) {setInterval(() => {if (executing.length > 0) return; // 还有正在执行的渲染 不反复增加
this.renderChartList();}, s);
},
// 初始化图表
initChart(domId) {return new Promise((resolve) => {if (!this.chartObjs[domId]) {this.chartObjs[domId] = echarts.init(document.getElementById(domId));
}
const option = {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {type: "value",},
series: [
{
data: this.chartData,
type: "line",
},
],
};
this.chartObjs[domId].clear();
this.chartObjs[domId].setOption(option);
this.chartObjs[domId].on("finished", () => {resolve(domId);
});
});
},
},
};
</script>
<style scoped>
.chart {
float: left;
width: 360px;
height: 300px;
margin: 10px;
border: 2px solid #ff9900;
}
</style>
小彩蛋
小彩蛋✌️, 红盾
压服 张三
买了个高性能的电视机 完满解决 …
如有不对, 欢送斧正 🌟 感觉有帮忙, 欢送三连🌟