防抖debounce和节流throttle
大纲
一、出现缘由
二、什么是防抖debounce和节流throttle
三、应用场景
3.1防抖
3.2节流
一、出现缘由
前端开发中,有一部分用户行为会频繁触发事件,而对于DOM操作,资源加载等耗费性能的处理,很可能导致卡顿,甚至浏览器崩溃,防抖和节流就是为了这一类的问题出现的前端优化技术。
二、什么是防抖debounce和节流throttle
防抖debounce是函数在规定延迟时间内不被调用,才能再次被调用,如果在规定时间内调用,延迟重新开始计算;
节流throttle是在规定的延迟时间间隔后,函数才执行一次,以固定的频率被触发。
三、简单实现
3.1防抖
连续触发事件的时候,不会有任何反应,停止触发事件的多少秒,就会执行,在延迟时间内执行函数,就重新开始一个定时器。
function debounce(func,delay=300,timer=null){ return (...args){ clearTimeout(timer); timer=setTimeout(func.bind(null,...args,delay);) } } function query(){ //ajax } input.addEventListener('keyup',debounce(query))
3.2节流
触发事件的时候,执行一个函数,在之后的一段时间内进这个函数,会被return,真正的逻辑不能执行,定时器在一定时间后重置开关,再进来就能再次执行真正的逻辑了。
function throttle(func,delay=60){ let lock=false; return (...args)=>{ if(lock)reutrn; function(..args); lock=true; setTimeout(()=>lock=false,delay); } } function query(){ //ajax } decument.addEventListener('scroll',throttle(query));
四、应用场景
3.1防抖throttle的应用场景
防抖适用于window.onscroll事件,等到拉动动作结束后再触发一个事件,或者拖拽
_.throttle=function(func,wait,options){ var context,args,result; var timeout=null; var previous0; if(!options)options={} var later=function(){ previous=options.leading===false:0:new Date(); timeout=null; result=func.apply(context,args); if(!timeout)context=args=null; } return fucntion(){ var now=new Date(); if(!previous && option.leading===false)previous=now; var remaining=wait-(now-previous); context=this; args=arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; } }
3.2节流throttle的应用场景
下面具体讲一个例子
假设我们网站有一个搜索框,用户输入文本我们自动会联想匹配除一些结果供用户选择,我首先想到的做法是监听keypress事件或change事件,然后ajax请求数据,但是当用户快速输入的时候,就会瞬间触发一连串的请求,这无疑不是我们想要的,我们想要的是用户停止输入的时候才去触发查询的请求,这时候函数防抖可以帮助我们。
_.throttle=function(func,wait,immediate){ var timeout,args,context,timestamp,result; var later=function(){ //如果没有超过等待时间,就接着设置一个定时器,时间是delay的时间减去方法执行的时间 var last=new Date().getTime() - timestamp; if(last<wait && last>=0){ timeout=setTimeout(later,wait-last); }else{ //如果已经过wait时间了,就直接清除定时器,给result赋值 clearTimeout(timeout); if(!immediate){ result=func.apply(context.args); if(!timeout)context=args=null; } } } return function(){ context=this; args=arguments; timestamp=new Date().getTime(); var callNow=immediate && timeout; //如果是立即执行或者之前执行过还没过延迟时间 if(!timeout)timeout=setTimeout(later,wait); //如果之前没执行过,现在开始定时任务,如果执行过,timeout存在,就返回个空的result. if(callNow){ //如果是立即执行,就立即执行fn,这时候 console.log(1); result=func.apply(context,args); context=args=null; } return result; } } function query(){ //ajax console.log('ajax'); } $("#search").keypress(_.throttle(query,300));