无论是公司项目,还是个人项目,往往会遇到这样的一些造成回调开销加大的问题,比如重复提交、快速点击、高频事件等。
问题
PC端以资产配置项目为例,存在的问题:
1)用户在搜索客户信息输入实时触发搜索,导致多次提交。
2)用户搜索查询时,第一次查询请求时间比较长,然后第二次请求可能比第一次返回先,导致查询结果和筛选不对应的情况。
3)调整浏览器窗口大小的时候,触发resize 事件,这时echart重绘的频率控制。
小程序端以答题考试知识库项目为例,存在的问题:
1)重复提交
单位时间内,多次请求,造成的后台重复录入现象。
2)快速点击
单位时间内,快速点击查询或者答题,触发异步事件,造成数据与ui混乱的现象。
3)高频事件
单位时间内,触发的各类事件,且响应速度低于请求,造成的卡顿或其他混乱现象。
知识库:
人物评选投票:
解决方案
一般在项目中比较常见用的方式是事件或函数的节流(throttle)与防抖(debounce)。
通常,scroll 事件,resize 事件、鼠标事件(比如 mousemove、mouseover 等)、键盘事件(keyup、keydown 等)都存在被频繁触发的风险。频繁触发回调导致的大量计算会引发页面的抖动甚至卡顿。
为了规避这种情况,我们需要一些手段来控制事件被触发的频率。然而,节流(throttle)与防抖(debounce)是我们在日常开发中常用的优质代码片段,能够给我们的项目带来性能的提升。
节流(throttle)与防抖(debounce)的本质
这两个东西都以闭包的形式存在。
它们通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。
节流(throttle)
节流:每间隔某个时间去执行某函数。
节流(throttle) 的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。
function throttle(fn: Function, delay: number) {let timer: any = null;return function () {if (timer) return;//如果定时器还在,说明上一次延迟执行还没有完成,则不再执行timer = setTimeout(() => {fn.apply(this, arguments);timer = null;}, delay);};
}
防抖(debounce)
防抖:将几次操作合并为一次操作进行。
防抖(debounce)的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。
function debounce(fn: Function, delay: number) {let timer: any = null;return function () {if (timer) {clearTimeout(timer);//如果方法多次触发,则把上次记录的延迟执行代码清掉,重新开始}timer = setTimeout(() => {fn.apply(this, arguments);timer = null;}, delay);};
}
总结
防止事件频繁触发回调函数的方法有很多,其中防抖和节流是现在比较主流的处理方式。
节流和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖动只是在最后一次事件后才触发一次函数。