节流定义了某些频繁操作的事件会影响性能,“throttle”用于控制响应的时间间隔,当事件触发时,相应的函数将不会它不会立即触发,而是按照特定的时间间隔执行,每当达到执行响应间隔时就会执行响应函数。节流案例在网游《飞机大战》中,键盘按键可以用来发射子弹。如果你不停地快速敲击键盘,飞机就不会连续开火,而是按照一定的时间间隔控制子弹和子弹。距离。例如,如果系统设置为每秒发射一次子弹,即使您在一秒钟内敲击键盘20次,也只会发射一颗子弹。节流使用场景在程序设计过程中,“节流”可以用在很多场景中。频繁在输入框输入,频繁点击搜索按钮,提交信息触发事件。监听浏览器的滚动事件,监听浏览器的缩放事件。在不使用节流的情况下,这里模拟一个商品搜索框。我们需要将内容调用接口与用户输入相关联。查询给用户搜索提示。不使用防抖时,我们直接将函数绑定到对应的事件上。//html//js代码constinputEl=document.querySelector("input");letcount=0;functioninputEvent(event){console.log(`${++count}次输入,得到内容是:${event?.target?.value}`);}inputEl.oninput=inputEvent;在输入框中输入“javascriptcsses6”,一共16个字符,所以方法被调用了16次。这种方式性能很低,因为每次输入一个字符都会调用接口,给服务器造成很大的压力。“节流”功能按照指定的时间执行函数,避免多次执行造成的资源浪费。自定义节流函数节流函数的原理是按照指定的时间间隔执行函数。Step1:基础版throttling实现了对函数本次执行与上次执行时间间隔的判断,如果超过指定的时间间隔则执行该函数。functionthrottle(fn,interval){//将初始时间设置为0letstartTime=0;const_throttle=function(){//获取当前时间letcurrentTime=newDate().getTime();//获取剩余时间(当前时间与指定区间的距离)letrestTime=interval-(currentTime-startTime);//当剩余时间小于等于0时,执行函数if(restTime<=0){//执行传入的函数fn();//将当前时间赋给初始时间startTime=currentTime;}};return_throttle;}inputEl.oninput=throttle(inputEvent,2000);这里指定的时间间隔为2秒,即函数每2秒执行一次。但是这时候我们发现参数并没有传递过来,其实这个的方向是错误的。第二步:扩展this和参数通过apply方法改变this的方向,传参functionthrottle(fn,interval){//设置初始时间为0letstartTime=0;const_throttle=function(...args){//获取当前时间letcurrentTime=newDate().getTime();//获取剩余时间(当前时间与指定间隔距离)letrestTime=interval-(currentTime-startTime);//当剩余时间小于等于0时,执行函数if(restTime<=0){//改变this的方向,通过apply传递参数fn.apply(this,args);//将当前时间赋给初始时间startTime=currentTime;}};return_throttle;}至此,this和参数就可以获取了~至此,节流的大部分使用场景已经实现,后面的函数会比较复杂。第三步:函数立即执行在上面的函数定义中,当输入第一个字符时,函数会大概率执行,因为输入第一个字符的时间减去初始化0秒的时间一般大于设置时间间隔。如果觉得输入第一个字符时的函数执行没有必要,可以自定义参数来控制函数是否立即执行。参数leading控制函数的立即执行,默认为true。functionthrottle(fn,interval,options={}){让startTime=0;//leading默认值设置为trueconst{leading=true}=options;const_throttle=function(...args){让currentTime=newDate().getTime();//当不需要立即执行时,将初始值为0的startTime修改为当前时间if(!leading&&!startTime){startTime=currentTime;}让restTime=interval-(currentTime-startTime);如果(restTime<=0){fn.apply(this,args);开始时间=当前时间;}};return_throttle;}//传入前导参数inputEl.oninput=throttle(inputEvent,2000,{leading:false,});这样,它会在执行第一个函数调用之前等待2s。第四步:函数“throttling”的最后一次执行只与函数的间隔时间有关,与最后一次字符输入的完成无关。所以在输入最后一个字符后,如果距离上次函数调用的时间间隔不在指定的时间间隔内,则此时函数不会执行。如果需要执行,需要自定义参数来控制函数执行。函数的最后执行由参数trailing控制,默认为false。当函数需要最后执行时,在每个时间间隔执行前设置定时器,当函数执行完该时间间隔后清零定时器,如果最后一个字符后未到指定间隔则执行定时器在内容中输入。functionthrottle(fn,interval,options={}){让startTime=0;//设置一个定时器lettimer=null;//leading的默认值设置为true,trailing的默认值设置为falseconst{leading=true,trailing=false}=options;const_throttle=function(...args){让currentTime=newDate().getTime();//当不需要立即执行时,修改初始值为0的startTime为当前时间if(!leading&&!startTime){startTime=currentTime;}让restTime=interval-(currentTime-startTime);if(restTime<=0){//如果计时器存在则清除它if(timer){clearTimeout(timer);定时器=空;}}fn.apply(this,args);开始时间=当前时间;//执行完成后,后面的定时器代码将不再执行,避免重复执行return;}//如果需要最后一次执行if(trailing&&!timer){//设置定时器timer=setTimeout(()=>{timer=null;fn.apply(this,args);//当需要时立即执行,开始时间赋值为当前时间,否则,赋值为0startTime=!leading?0:newDate().getTime();},restTime);}};return_throttle;}//传入前导和尾随参数inputEl.oninput=throttle(inputEvent,2000,{leading:false,trailing:true,});此时输入最后一个字符,等待定时器设置的时间间隔(restTime),函数会再次执行第五步:取消函数可能会有这样的场景,当用户在搜索的时候点击取消,或者关闭页面,此时不需要发送请求。我们添加一个取消按钮,点击后终止操作。//html//javascriptfunctionthrottle(fn,interval,options={}){letstartTime=0;//设置一个定时器lettimer=null;//leadingdefault值设置为true,trailing默认值设置为falseconst{leading=true,trailing=false}=options;const_throttle=function(...args){让currentTime=newDate().getTime();//不需要立即执行时,将初始值为0的startTime修改为当前时间if(!leading&&!startTime){startTime=currentTime;}让restTime=interval-(currentTime-startTime);if(restTime<=0){//当有定时器时,清除定时器if(timer){clearTimeout(timer);定时器=空;}fn.apply(this,args);开始时间=当前时间;代码避免重复执行返回;}//如果需要最后一次执行if(trailing&&!timer){//设置定时器timer=setTimeout(()=>{timer=null;fn.apply(this,args);//当需要时立即执行,开始时间赋值当前时间,否则赋值0星tTime=!leading?0:新日期().getTime();},休息时间);}};//在函数对象上定义一个取消方法_throttle.cancel=function(){//当有定时器时,清除if(timer){clearTimeout(timer);定时器=空;//重置开始时间startTime=0;}};return_throttle;}//获取dom元素constinputEl=document.querySelector("input");constcancelBtn=document.querySelector("按钮");const_throttle=throttle(inputEvent,2000,{leading:false,trailing:true,});//绑定事件inputEl.oninput=_throttle;cancelBtn.onclick=_throttle.取消;当点击取消时,定时器的内容将不再执行。第六步:函数返回值上面的“throttling”函数执行后没有返回值。如果需要返回值,回调函数有两种形式通过在参数中传递回调函数来获取返回值。functionthrottle(fn,interval,options={}){让startTime=0;//设置一个定时器lettimer=null;//leading默认值设置为true,trailing默认值设置为false,传入回调函数接收返回值const{leading=true,trailing=false,callbackFn}=options;const_throttle=function(...args){让currentTime=newDate().getTime();//当不需要立即执行时,修改初始值为0的startTime为当前时间if(!leading&&!startTime){startTime=currentTime;}让restTime=interval-(currentTime-startTime);if(restTime<=0){//当有定时器时If(timer){clearTimeout(timer);定时器=空;}}//获取函数执行结果constresult=fn.apply(this,args);//执行传入的回调函数if(callbackFn)callbackFn(result);开始时间=当前时间;//执行完成后,后面的定时器代码将不再执行,避免重复执行return;}if(trailing&&!timer){timer=setTimeout(()=>{timer=null;//获取执行函数的结果constresult=fn.apply(this,args);//执行传入的回调函数if(callbackFn)callbackFn(result);//当需要立即执行时,开始时间赋值为当前时间,否则赋值为0startTime=!leading?0:newDate().getTime();},休息时间);}};//在函数对象上定义一个取消方法_throttle.cancel=function(){if(timer){//当有定时器时,清除它clearTimeout(timer);定时器=空;//重置开始时间startTime=0;}};return_throttle;}constinputEl=document.querySelector("input");constcancelBtn=document.querySelector("button");//传入回调函数用于接收返回值const_throttle=throttle(inputEvent,2000,{leading:false,trailing:true,callbackFn:(value)=>{console.log("获取返回值",value);},});inputEl.oninput=_throttle;cancelBtn.onclick=_throttle.cancel;每次执行响应函数,都会执行一次回调函数Promise通过返回promise函数获取返回值functionthrottle(fn,interval,options={}){letstartTime=0;//设置一个定时器lettimer=null;//leading默认值设置为true,trailing默认值设置为falseconst{leading=true,trailing=false}=options;const_throttle=function(...args){//通过promise返回结果returnnewPromise((resolve,reject)=>{letcurrentTime=newDate().getTime();//当不需要执行时立即将初始值为0的startTime更改为当前时间if(!leading&&!startTime){startTime=currentTime;}letrestTime=interval-(currentTime-startTime);if(restTime<=0){//当有定时器时,清除定时器通过resolveresolve(result);startTime=currentTime;return;}}if(trailing&&!timer){timer=setTimeout(()=>{timer=n;返回成功响应//获取函数执行结果constresult=fn.apply(this,args);//通过resolve返回一个成功的响应resolve(result);//当需要立即执行时,开始时间赋值当前时间,否则赋值0startTime=!leading?0:新日期().getTime();},休息时间);}});};//在函数对象上定义一个取消方法_throttle.cancel=function(){if(timer){//当有定时器时,清除它clearTimeout(timer);定时器=空;//重置开始时间startTime=0;}};return_throttle;}//获取dom元素constinputEl=document.querySelector("input");constcancelBtn=document.querySelector("按钮");const_throttle=throttle(inputEvent,2000,{leading:false,trailing:true,});//apply用于将this指向输入元素constpromiseCallback=function(...args){_throttle.apply(inputEl,args).then((res)=>{console.log("promisecallback",res);});};//绑定事件inputEl.oninput=promiseCallback;cancelBtn.onclick=_throttle.cancel;promise调用then方法获取返回值开发中使用throttle函数优化项目的性能可以像上面那样自定义,也可以使用第三方库。关于防抖功能,可以参考这篇文章,自定义防抖功能满足复杂需求的五步以上就是防抖功能的内容,关于js进阶,还有很多需要的东西开发者掌握,可以看我写的其他博文,不断更新中~
