JavaScript之防抖(Debounce)和节流(Throt

2019-08-05  本文已影响0人  HalShaw
防抖与节流

一、背景

防抖和节流是两种不同的控制一个函数执行次数的方法,其目的都是为了节约计算机资源。
当我们操作DOM的时候,加上节流或者防抖就非常有必要,因为众所周知,操作DOM的开销是非常大的,所以要尽可能减少DOM操作次数。

看下面的在线例子:
https://codepen.io/dcorb/pen/PZOZgB

当鼠标滚动或者拖拽的时候可以轻易地每秒触发30个事件,而且在移动端慢速滚动可以达到每秒100次,要是这么短的时间内每次都触发一个或多个函数,那浏览器应该会卡死崩溃的,所以这就引出了防抖节流

二、防抖(Debounce)

防抖允许我们把多次连续的调用放到一次里面去执行,在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

防抖

试想一下,你正身处一部电梯之中,电梯门开始关闭,然后突然又来了一个人,按下了开门键,电梯门开了,他顺利上了电梯,然后电梯又开始缓慢关闭,但是此时又匆匆跑来了另一个人,电梯门又开了,此时,快要迟到的你心里面开始有点不满了。但是,这其实就是一个防抖的实际例子,电梯尽可能的使其资源利用率达到最高,在尽可能的情况下,载上尽可能多的人。

下面是一个例子:
https://codepen.io/dcorb/pen/KVxGqN

function debounce(func,delay) {
    let timer;
    return function(...args) {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            func.apply(this,arguments);
        },delay)
    }
}

Lodash中实现 _.debounce_.throttle 的功能很全面,可以直接使用,其中的 throttle函数是使用 _.debounce 新增 maxWait` 选项来实现的, 有兴趣可以自行查看 源码

// Based on http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
$(document).ready(function(){
  
  var $win = $(window);
  var $left_panel = $('.left-panel');
  var $right_panel = $('.right-panel');
  
  function display_info($div) {
    $div.append($win.width() + ' x ' + $win.height() +  '<br>');
  }
                
  $(window).on('resize', function(){
    display_info($left_panel);
  });
  
  $(window).on('resize', _.debounce(function() {
    display_info($right_panel);
  }, 400));
});

可以用来监听浏览器窗口尺寸变化事件。
https://codepen.io/dcorb/pen/XXPjpd

$(document).ready(function(){

  var $statusKey = $('.status-key');
  var $statusAjax = $('.status-ajax');
  var intervalId;
  
  // Fake ajax request. Just for demo
  function make_ajax_request(e){
      var that = this;
      $statusAjax.html('That\'s enough waiting. Making now the ajax request');
     
      intervalId = setTimeout(function(){
         $statusKey.html('Type here. I will detect when you stop typing');
         $statusAjax.html('');
         $(that).val(''); // empty field
      },2000);
    }
  
  // Event handlers to show information when events are being emitted
    $('.autocomplete')
      .on('keydown', function (){  
      
        $statusKey.html('Waiting for more keystrokes... ');
      clearInterval(intervalId);             
      })
     
  
  // Display when the ajax request will happen (after user stops typing)
    // Exagerated value of 1.2 seconds for demo purposes, but in a real example would be better from 50ms to 200ms
  $('.autocomplete').on('keydown',
       _.debounce(make_ajax_request, 1300));
  });

当用户输入并且发起Ajax请求的时候,_.debounce可以用来实现防抖,比如间隔2秒未检测到用户输入才发起请求。

三、节流(Throttle)

var throttle = function(func, delay) {
    var timer = null;
    return function() {
        if (!timer) {
            timer = setTimeout(function() {
                func.apply(this,arguments);
                timer = null;
            },delay);
        }
    }
}

另一种定时器写法:

let isAllow = true;
function throttle() {
    let fun = function() {
        if (!isAllow)
            return;
        let timer = setTimeout(() => {
            console.log("throttle");
            clearTimeout(timer);
            timer = null;
            isAllow = true;
        },1000);
    };
    fun();
}

二者的区别是后者使用了isAllow标志位来判断是否需要执行函数。

requestAnimationFrame

requestAnimationFrame是另一种限制函数执行次数的方式,可以认为与_.throttle类似,但是其拥有更高的准确度,因为其本身就是为了更好的精确度而生的原生API。

requestAnimationFrame的优点

requestAnimationFrame的缺点

requestAnimationFrame实例

_.throttle相比,同时设置 16ms, 相同的性能环境下,rAF 可以在更复杂的情况下拥有更好的结果。

https://codepen.io/dcorb/pen/pgOKKw

四、结论

五、参考

https://css-tricks.com/debouncing-throttling-explained-examples/

上一篇 下一篇

猜你喜欢

热点阅读