我是如何控制1000 个 Echarts 实例同时渲染 最大并发的
source link: https://segmentfault.com/a/1190000041315443
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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>
小彩蛋✌️, 红盾
说服张三
买了个高性能的电视机 完美解决...
如有不对,欢迎指正🌟 觉得有帮助,欢迎三连🌟
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK