什么是分时函数
将一个庞大的任务按照一定的时间段拆分成一个一个的小任务,以免任务太过庞一次执行所所造成的页面卡顿或假死。
举个例子:在页面中的树在网页中以树形结构显示分层数据,如果某一个列表有成百上千的数据,如果将每一个数据看成一个节点,在页面中渲染这个列表的时候,可能要一次性的网页面中创建成百上千个节点,
在短时间内往页面中大量添加 DOM 节点显然也会让浏览器吃不消,看到的结果往往就是浏览器的卡顿甚至假死。
const treeArr = [];
for (let i = 1; i <= 1000; i++) {
treeArr.push(i); // 假设 treeArr 装载了 1000 个数据
}
renderTreeList(treeArr);
function renderTreeList(data) {
for (let i = 0, l = data.length; i < l; i++) {
let div = document.createElement('div');
div.innerHTML = i;
document.body.appendChild(div);
}
};
这个问题的解决方案之一就是使用分时函数,分时函数是一种使用定时器分割循环的技术,为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目进行处理,接着再设置另一个定时器。
在分时函数模式中,数组变量本质上就是一个"待办事宜"列表,它包含了要处理的项目。使用shift()方法可以获取队列中下一个要处理的项目,然后将其传递给某个函数。如果在队列中还有其他项目,则设置另一个定时器,并通过arguments.callee调用同一个匿名函数。
分时函数的重要性在于它可以将多个项目的处理在执行队列上分开,在每个项目处理之后,给予其他的浏览器处理机会运行,这样就可能避免长时间运行脚本的错误。一旦某个函数需要花 50 ms 以上的时间完成,那么最好看看能否将任务分割为一系列可以使用定时器的小任务。
如何使用分时函数
基于这个思想,我们先写一个demo,例如:
<body>
<div id="myDiv"></div>
<script>
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
timeChunk(data, printValue);
function printValue(item) {
const div = document.getElementById('myDiv');
div.innerHTML += `<p>${item}</p>`;
}
function timeChunk(array, process) {
setTimeout(function () {
// 取出下一个条目并处理
const item = array.shift();
// 将这个条目放入页面
process(item)
// 若还有条目,再设置另一个定时器
if (array.length > 0) {
setTimeout(arguments.callee, 200);
}
}, 100);
}
</script>
</body>
这是将数组的每一项看成是每一个小任务,然后依次执行渲染。
下面我们再来看看一个详细的实现过程:
<body>
<script>
// 创建数据数组
const ary = [];
for (let i = 1; i <= 1000; i++) {
ary.push(i);
}
// 利用这个时间函数把我们需要渲染的数据数组,添加节点的方法以及每次添加的个数
const renderFriendList = timeChunk(ary, function (n) {
const div = document.createElement('div');
div.innerHTML = n;
document.body.appendChild(div);
}, 8);
// renderFriendList中保存的是timeChunk返回出来的匿名函数引用
renderFriendList();
// 该分时函数接收 3 个参数:数据、创建节点逻辑的函数(可传入节点内容)、每一批创建的节点数量
// 该分时函数将返回一个计时函数,根据数据是否已经创建完节点来决定是否停止
function timeChunk(ary, fn, count) {
//将数组的长度保存,提高性能
let len = ary.length;
const start = function () {
// 创建规定数量的节点
for (let i = 0; i < Math.min(count || 1, len); i++) {
//弹出传入数量的数据和数组长度中的最小值,
let item = ary.shift();
fn(item);
}
};
return function () {
let t = setInterval(function () {
// 如果全部节点都已经被创建好
if (len === 0) {
return clearInterval(t);
}
start();
}, 200); // 分批执行的时间间隔,也可以用参数的形式传入
};
};
</script>
</body>
本文章取自本人JS语言导师谢老师的学习总结,同时也感谢谢老师对我的谆谆教诲,感谢他带我走上前端这条道路,并让我为之不断向前