节流定义
当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内又触发了该事件,则什么也不做。
(类似于发技能,发出一次技能后,要等待技能冷却时间结束,在此期间的点击都会被忽略,当技能冷却时间结束,点击即可触发技能; 触发技能后,接着等待技能冷却时间结束,在此期间的点击都会被忽略,当技能冷却时间结束;点击即可触发技能;...)
两种实现方法
定时器版
先设置一个局部变量timer,初始化为null,第一次触发函数后,设置定时器的同时赋值给timer, 定时器任务完成期间的调用会被忽略不执行任何操作,直至定时器完成将变量timer置为null;再次调用时,再次定时器,设置定时器的同时赋值给timer, 定时器任务完成期间的调用会被忽略不执行任何操作,直至定时器完成将变量timer置为null;...。
function throttle (fn, wait) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
timer = null;
}, wait)
}
}
}
特点: 当第一次
触发事件时,不会立即执行函数
,到了规定时间后才会执行。 之后连续频繁地触发事件,也是到了规定时间才会执行一次(因为定时器)。在等待经过时间间隔的期间,第2次、第3次的调用会被忽略,当等待过规定时间后,将执行第1次触发放入的回调;接着,第4次触发会被放入定时器的回调函数中,在等待经过时间间隔的期间,第5次的调用会被忽略,等待过规定时间后,将执行第4次触发放入的回调;接着,最后一次触发也就是第6次触发会被放入定时器的回调函数中,等待过规定时间后,将执行第6次触发放入的回调。
当最后一次停止触发后,由于定时器的延时,还会执行一次回调函数(那也是上一次成功成功触发执行的回调,而不是你最后一次触发产生的)。一句话总结就是延时回调,你能看到的回调都是上次成功触发产生的,而不是你此刻触发产生的。
时间戳版
先设置一个last记录上一次触发的时间戳,在返回的函数内部设置一个变量now记录此刻调用的时间,判断now-last大于设置时间则执行函数,并将last设置为now。否则什么也不做。
function throttleTwo (fn, wait) {
let last = 0;
return function (...args) {
let now = Date.now();
let context = this;
if (now-last >= wait) {
fn.apply(context, args)
last = now;
}
}
}
特点: 第一次
会立即执行,之后连续频繁地触发事件,也是超过了规定时间才会执行下一次。最后一次触发事件,也不会执行(说明:如果你最后一次触发时间大于规定时间,这样就算不上连续频繁触发了).
第一次
触发函数会立即执行,之后等待经过时间间隔的期间,第2次、第3次的调用会被忽略,当等待过规定时间后,将第4次触发函数立即执行;之后等待经过时间间隔的期间,第5次、第6次的调用会被忽略,当等待过规定时间后,将第7次触发函数立即执行;由于第8次调用在时间间隔内,所以不会执行。
这两者最大的区别:是时间戳版的函数触发是在规定时间开始的时候,而定时器版的函数触发是在规定时间结束的时候。
进阶实现
时间戳+定时器版:
总体上还是时间戳的思路,用定时器去执行最后一次的触发。
function throttleThree (fn, wait) {
let last = 0;
let timer = null;
return function (...args) {
let context = this;
let now = Date.now();
clearTimeout(timer)
if (now - last > wait) {
fn.apply(context, args)
last = now;
} else {
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
}
总结
函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。
应用场景
两个条件: 1,客户连续频繁地触发事件(就是事件触发的时间间隔至少是要比规定的时间要短) 2,客户不再只关心"最后一次"操作后的结果反馈.而是在操作过程中持续的反馈.
- 鼠标不断点击触发,点击事件在规定时间内只触发一次(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
- scroll,resize, touchmove, mousemove等极易持续性促发事件的相关动画问题,降低频率
- DOM 元素的拖拽功能实现(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 搜索联想(keyup)