防抖和节流
防抖、节流主要是用来处理某些操作,导致函数和接口的过频响应。其主要原理是利用适当的延迟和屏蔽,来避免出现过度调用出现卡顿和性能的损耗。下面我将分别对这两种方法的使用、适用场景和特性进行分析。
防抖(debounce)
其实debounce更贴切的说应该是一种截留。当程序为了获取用户的某项操作是,为了能够准确的得到数据变化,不得不实时监测一些数据从而导致函数和接口的频繁响应。然而有些时候对于用户和程序起到真正有作用的,却往往只有阶段性的变化结果或者是最终结果。
例如:1.滚动条滚动触发 scroll 计算 显示置顶按钮
2.搜索引擎的输入框 change 的智能提示
这时候我们就可以当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作 这就是debounce的基本思路。当然不排除某些时候也会有执行初期 立即响应的情况然后再开始延期,对于这种情况我们称它为前缘debounce。
延迟debounce 示意图:
前缘debounce 示意图:
防抖示例 代码
var timer = null;
var movefun=function(){
timer && clearTimeout(timer);
timer = setTimeout(function() {
console.log('触发事件');
}, 1000);
}
window.document.onmousemove=movefun
增加前缘触发 代码
var debounce = (fn, wait, immediate=false) => {
let timer, startTimeStamp=0;
let context, args;
let run = (timerInterval)=>{
timer= setTimeout(()=>{
// 定时器执行 获取当前时间
let now = (new Date()).getTime();
// 当前时间 和最后一次的触发时间差
let interval=now-startTimeStamp
//这里比较难理解 这个 其实是 将定时器的创建时的定时时间 与最后一次触发的时间与当前时间差的大小判断
//其实这个是可以用==来判断创建当前定时器的那次触发就是最后一次触发 单为避免执行误差 所以使用了<
if(interval<timerInterval){ // the timer start time has been reset,so the interval is less than timerInterval
console.log('debounce reset',timerInterval-interval);
startTimeStamp=now;
// 重置定时器 并以最后一次触发时间和最初的定时时间做差值以确保准确
run(wait-interval); // reset timer for left time
}else{
if(!immediate){
fn.apply(context,args);
}
clearTimeout(timer);
timer=null;
}
},timerInterval);
}
return function(){
context=this;
args=arguments;
let now = (new Date()).getTime();
//每次触发 事件记录当前触发时间
startTimeStamp=now; // set timer start time
if(!timer){
console.log('debounce set',wait);
if(immediate) {
fn.apply(context,args);
}
//第一次创建定定时器 时间为 创建时传入的 wait 时间
run(wait); // last timer alreay executed, set a new timer
}
}
}
window.document.onmousemove=debounce(function(){alert("111111")},1000)
节流(throttling)
在一定时间内只准许执行一次,即使过多操作也会被屏蔽 。直到上一次周期方法结束,下一次才会被准许调用。 这个操作其实更像是一个定时锁 。
例:获取验证码 一般都会有个60秒倒计时 60秒以后方可再次获取
节流 示意图
前缘节流
节流示例 代码
var lastTime = null, nowTime = null,gapTime=1000;
var moveFunc = function() {
nowTime = new Date().getTime();
if(nowTime - lastTime > gapTime || !lastTime) {
console.log('请求接口返回搜索内容。。。');
lastTime = nowTime;
}
}
window.document.addEventListener("mousemove",moveFunc )
增加前缘触发 代码
var throttling = (fn, wait, immediate) => {
let timer, timeStamp=0;
let context, args;
let run = () => {
timer=setTimeout(()=>{
if(!immediate){
fn.apply(context,args);
}
clearTimeout(timer);
timer=null;
},wait);
}
return function () {
context=this;
args=arguments;
if(!timer){
console.log("throttle, set");
if(immediate){
fn.apply(context,args);
}
run();
}else{
console.log("throttle, ignore");
}
}
}