最近有个乱七八糟的产品,又加了个乱七八糟的需求,结果还被怼了乱七八糟,阿西吧!
在页面设置两个定时器,一个倒计时,另一个轮询,有冲突(臆想)。
基础 setTimeout
最基础的定时器是 setTimeout,指定 xx ms 后执行。
来看它的一个缺陷:
for () {
// 运行了 1s
}
// 指定 100ms 后执行
setTimeout('console.log(1)', 100);
打印日志的时间在 1s 后,而不是我们预期的 100ms。
众所周知 js 是单线程的,在线程被占用的情况下,无法进行另一个操作,只能加入到执行队列中,而一旦线程空闲时,会立即执行队列中的操作。
因此,setTimeout 的执行时间是不太精确的。
重复 setInterval
setInterval 主要用来重复执行,指定 xx ms 间隔。
它是在各个时间点加入到执行队列中,等进程空闲后执行。可能出现某个间隔被跳过的情况:
setInterval(function longTime() {
for () {
// 运行了 300ms
}
}, 100);
longTime 运行期间,其它 longTime 副本也被加入到队列中,多个只执行最先加入的那个,其它将被跳过。
这个小缺陷可以用高级点的技巧避免:
setTimeout(function foo() {
// 一些操作
// 递归,做到类似 setInterval 的效果
// 且比 setInterval 更可靠,保证了间隔不被跳过
setTimeout(foo, 100);
}, 100);
应用
定时器的延迟执行功能,可应用到许多场景,例如倒计时等等。更多的是性能优化这一块。
数组分块 Yielding Processes
假设有这样的场景:
const arr = new Array(1000);
for (let i = 0; i < arr.length; i++) {
// 操作数组的每一项,同步的
// 运行了 100ms
}
// 总共耗时 1000x0.1 = 100s
上面的场景,界面在 100s 内都无响应,因为 js 线程一直被占用,无法执行其它操作。
使用定时器把数组分成一块一块处理:
const arr = new Array(1000);
const chunkSize = 1; // 分块大小
setTimeout(function foo() {
const arrChunk = arr.splice(0, chunkSize);
// 操作分块后的数组 arrChunk
// 运行了 100ms * chunkSize
if (arr.length > 0) {
// 100ms 的缓冲期,让界面不至于失去响应
setTimeout(foo, 100);
}
}, 100);
节流和防抖 throttle & debounce
节流和防抖两个概念太相似了,每次都会把它俩搞混。。。都是减少触发频率,提高性能,最主要的差别应该是触发时间。
- 节流触发时间在前。类似
setInterval的执行机制,触发多次时,在指定的时间间隔内,只有第一次的才会执行,其它的跳过(手动)。 - 防抖触发时间在后。与
setTimeout类似,延迟执行,触发多次时,只执行最后一个,其它的被清除(手动)。
应用场景上也能混用,,,大概了解下就行了
// 大多用在 scroll 上的节流
// 也可以使用 px 单位,比如 10px
HTMLElement.addEventListener('scroll', function throttle() {
const ts = Date.now();
// 上次调用时间过了 100ms
if (ts - lastInvokeTs > 100) {
lasInvokeTs = ts;
// 一些操作
}
});
// 大多用在 keyup、resize 上的防抖
HTMLElement.addEventListener('keyup', function debounce() {
// 延迟到触发时 100ms 后
clearTimeout(timer);
timer = setTimeout(function () {
// 一些操作
}, 100);
});
针对 Css 渲染方面的节流可以考虑原生更流畅的 API requestAnimationFrame:
HTMLElement.addEventListener('scroll', function foo() {
if (!changing) {
requestAnimationFrame(function () {
// 一些操作
changing = false;
});
}
changing = true;
});
css-tricks 上更好的一篇解释防抖节流的文章。
The End
一般来说页面里同时设置几个定时器问题不大,js 的执行速度很快,误差也大不到哪去,况且 ms 级别的误差也感觉不到。
节流和防抖还是比较混,阿西吧!