6

函数节流介绍

页面在绑定resize,keydown或者mousemove这些能连续触发的事件时,用户只要很常规的操作,就能连续触发多次绑定的方法。当绑定方法里面存在大量的类似于DOM操作这种极其消耗性能的代码时,会直接导致页面运行的卡顿。这个时候就会用到函数节流。

函数节流的实现

函数节流最普通的实现就是通过取摩操作来过滤部分执行。参考代码如下

javascriptvar mousemoveCount = 0;
function mousemoveListener(e){
  mousemoveCount++;
  if(mousemoveCount % 2 === 0){
    return;
  }
  console.info('业务逻辑');
}

当第一次触发并执行mousemoveListener事件时,会打印“业务逻辑”;紧接着第二次执行mousemoveListener事件时,由于mousemoveCount为2,会直接return掉,并不会打印“业务逻辑”。这样子,就实现了函数节流,存在复杂计算的业务逻辑运行次数减半了。

但是这种实现存在两个问题:

  1. 方法的执行频率(或者说帧率)是不可控的。比如mousemove事件,执行频率由鼠标移动速度决定,由上面这种方式实现,频率还是由鼠标移动速度决定。
  2. 最后一次触发可能未执行。比如当最后一次触发事件时,mousemoveCount是偶数,那么会直接return。如果业务需要最后一次必须执行业务逻辑,则会存在bug。

所以就有了下面的优化实现(throttle和debounce)。

throttle实现

throttle又叫函数节流,思路是控制某一个时间段(执行周期)内触发的事件,只会执行一次业务逻辑。代码如下:

javascriptvar lastMousemoveTime = 0, mousemoveTime = 100;
function mousemoveListener(e){
  var now = new Date().getTime();
  if(now - lastMousemoveTime <= mousemoveTime) {
    return;
  }
  lastMousemoveTime = now;
  setTimeout(function(){
    console.info('业务逻辑');
  }, mousemoveTime);
}

第一次触发mousemove会设置100ms后执行业务逻辑,在这之后的100ms里面触发的mousemove都不会触发业务逻辑。相当于控制了mousemove事件100ms触发一次,也就是10帧。

使用这种实现(throttle),可以做到触发频率可控。但当业务希望连续的触发事件只在之后一次触发后才执行业务逻辑,比如resize事件,只希望窗口变化结束后才进行业务逻辑的运行,throttle实现就不适用了。这个时候就需要使用到debounce

debounce实现

debounce又叫函数去抖动,思路是业务逻辑在resize不在触发后才执行。代码如下:

javascriptvar resizeTimer = null;
function resizeListener(e){
  if(resizeTimer) {
    clearTimerout(resizeTimer);
  }
  resizeTimer = setTimeout(function(){
    console.info('业务逻辑');
  }, 100);
}

但resize连续快速触发时,业务逻辑并不会执行。只有当最后一次触发resize后100ms,才执行业务逻辑。这种情况就能实现只在最后一次resize触发业务计算了。

underscore 中已经对throttle和debounce做了实现和封装, 有兴趣可以去查看源码。


chenhao_ch
2.1k 声望100 粉丝