# 前端防抖与节流

# 问题描述


什么是前端防抖和节流?它们的区别是什么?怎么使用的?


# 问题分析



# 解决方案


# 防抖


概念:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。


思路:每次触发事件的时候,清除定时器,然后从新设置新的定时器。


应用场景


  • 输入框search搜索联想,用户在不断输入值时触发input事件,用防抖来节约请求资源。
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次


基本vue使用


onDebounce() {
   // timer是该对象全局定义的,每次执行onDebounce函数,调用的相同的timer
   clearTimeout(this.timer); 
   this.timer = setTimeout(()=>{
       this.getAjax(); // 后台请求ajax信息
   }, 2000);
}, 


基本js代码


function debounce(fn) {
 let timeout = null; // 创建一个闭包,保存一个标记用来存放定时器的返回值
 return function () {
   clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
   timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
     fn.call(this);
   }, 500);
 };
}

document.getElementById('inp').addEventListener('input', debounce(getAjax)); // 防抖 


# 节流


概念:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。


简要思路:每此触发事件时判断是否定时器已满,满则重新定时,否则不做任何事。


具体思路1:设置一个触发标志flag=true,在事件里面判断:如果flag==true,进行一次定时任务,并将flag=false,在定时任务里面执行获取ajax的代码,并将flag=true;如果flag=false,直接return出去不做任何事情,直到定时器满。


具体思路2:不使用定时器,直接比较时间戳差值。


应用场景


  • 鼠标不断点击触发,click/mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断


思路1-vue代码


onThrottle() {
   if(!this.throttleFlag) return;
       
   this.throttleFlag = false;
   this.timer = setTimeout(()=>{
       this.getAjax();
       this.throttleFlag = true;
   }, 2000);
} 


思路1-js代码


function throttle(fn) {
 let canRun = true; // 通过闭包保存一个标记
 return function () {
   if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
   canRun = false; // 立即设置为false
   setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
     fn.apply(this, arguments);
     // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
     canRun = true;
   }, 500);
 };
}

window.addEventListener('resize', throttle(getAjax)); 


思路2-vue代码


onThrottle() {
   let now = Date.now();
   if (now - this.lastTime > 5000) { // 5s
       this.getAjax();
       this.lastTime = now; // update时间
   }
} 


思路2-js代码


 function throttle (fn) {
   let lastTime = 0;  // 上次触发的时间
   return function () {
       let now = Date.now();
       if (now - lastTime > 5000) {
          fn.call(this);
          lastTime = now; // update时间
       }
   }
} 


# 参考链接