这是我参与「第四届青训营」笔记创作活动的第4天
掘金中有很多关于前端渲染大量数据的博客和方法, 但却很少提到导入表格数据?
现在我的需求是这样的: 前端管理员需要导入本次所有的用户信息, 大概4000条左右。
最简单的做法是一次性全部给后端, 但 Mysql 执行插入操作也需要时间, 就可能会出现超过60s没有响应,浏览器就自动取消接口。如果是一条一条传入, 那么相当于4000次的交互, 少说也得10~30分钟左右吧。
这里我选择的方案是, 一次性传输300条数据, 并行执行3个请求, 最后总时间大概在8~15s左右。
第一步, 将所有数据拆分成300一组
这里就用 lodash 里的 chunk 方法了, 然后可以在里面进行处理
let arr = _.chunk(
data.map((item) => {
item.id = String(item.id);
item["pwd"] = encryptByAes256(item.id);
return item;
}),
300
);
第二步,有限制的并行执行
这里有两种方法:
- 在全局
axios处加个计数变量和数组, 每当一个请求发出时, 就+1, 完成时-1, 没有发送完的就存到数组, 到最后在检查数据发出剩下的。
第一种方法局限性较大, 且也不能复用, 之后就考虑把它封装出来, 最终是在 《node.js 设计模式》 一书中看到有限制的并行执行。
其函数代码如下
export default class handleTask {
constructor(maxCount) {
this.maxCount = maxCount;
this.pendingTask = []; // 待执行的任务
this.completed = 0;
this.count = 0;
}
run(task) {
if (this.count < this.maxCount) {
this.count++;
task().then(() => {
this.count--;
this.completed++;
console.log(this.completed);
if (this.pendingTask.length > 0) {
this.run(this.pendingTask.shift());
}
});
} else {
this.pendingTask.push(task);
}
}
}
它在构造函数中初始化了 4个变量,
- maxCount: 并行执行的最大数量
- pendingTask: 待执行的任务
- completeted: 完成的个数
- count: 当前正在执行的个数
原理也很简单:
- 即每次都判断是否 < maxCount
- 若小于, 就在执行前给 count++
- 执行完成后, count--, completed++, 并且检查pendingTask, 有数据的话就执行一个, 因为此时正好空出一个来
- 若大于, 则推入 pendingTask 数组里
- 若小于, 就在执行前给 count++
那么使用时, 我可以直接 for + run 方法即可。
for (let i = 0; i < arr.length; i++) {
han.run(upl(arr[i]));
}
function upl() {
let slice = Array.prototype.slice;
let [info] = slice.call(arguments);
return function () {
return new Promise((resolve) => {
stuAdd({ info }).then((res) => {
resolve();
console.log(res);
});
});
};
}
因为这里需要调用 2 次(一次传入参数, 一次执行 function), 就简单的进行下封装。
最后, 怎么监听所有请求都完成了呢?
我这里用的是 setInterval 轮询的方式每隔 1s 查询一次, 判断 han.completed === arr.length。
总结
关于前端导入大量数据到数据库, 由于自己团队中都是偏前端的, 所以解决方法也是重前端。 如果大家有什么其他更好的方法, 欢迎大家评论留言。
这篇文章已收录到我的个人博客中啦