头图

Demo address

Go to the demo address first

https://codepen.io/firstblood93/pen/QWqgVJL

Project Background

As leadership-oriented programming becomes more and more popular, Kanban project must be a must for every front-end development expert

Product manager Zhang San asked front-end development expert Hongdun to make a regular update of the company's financial expenditures

Red Shield operated like a tiger on chrome , and within two or three days, the project was completed and delivered to product manager Zhang San.

No Concurrency Control

Let's take a look at the approximate code situation of Red Shield at this time

<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>

As a result, Zhangsan played cards according to the routine, secretly installed a browser on the TV (extremely poor performance), and then entered the address xxx.com (self-improvement)...

the browser... Yes, 161e939ba2430b Red Shield's heart at this time

image.png

This time he thought of the back-end big brother Huang, Huang gave him an idea concurrency control

Implement control concurrency

implements concurrent

  1. First, let's implement a control concurrency function

Students who want to go directly to the theme can skip directly to complete code

/**
 * @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)
}
  1. Retrofit renderChartList function ( core)

    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 function

We must ensure a chart rendering is complete, the next and then perform a rendering, then we need to listen Echarts of finished event

initChart(domId) {
  // 我们 把它改造成一个 promise 函数
  return new Promise((resolve) => {
    ...
    // 核心代码 监听 echarts 的 finished
    this.chartObjs[domId].on("finished", () => {
      resolve(domId);// 对应 上一步的 `const res = await this.initChart(dom);`
    });
  });
}

4. intervalChartData function

We must ensure that all graph renderings are executed concurrently before entering the next timer logic
It is executing to judge the length of 061e939ba244ee (at this time, executing should be independent as a global variable)

intervalChartData(s) {
  setInterval(() => {
    if (executing.length > 0) return; // 还有正在执行的渲染 不重复添加
    this.renderChartList();
  }, s);
}

full code

Attach complete code

<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>

Easter eggs

Little Easter Egg✌️, Red Shield persuaded Zhang San to buy a high-performance TV, the perfect solution...

wrong, please correct me 🌟 find it helpful, welcome to 🌟


mikechen
1.1k 声望18 粉丝