GVKun编程网logo

JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)(js 函数节流)

15

对于JavaScript性能优化之函数节流感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解throttle与函数去抖,并且为您提供关于angular.js和vue.js中实现函数去抖(deb

对于JavaScript性能优化之函数节流感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解throttle与函数去抖,并且为您提供关于angular.js和vue.js中实现函数去抖(debounce)、JavaScript Debounce&Throttle、JavaScript Throttle & Debounce、Javascript Throttle & Debounce应用介绍的宝贵知识。

本文目录一览:

JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)(js 函数节流)

JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)(js 函数节流)

函数节流,简单地讲,就是让一个函数无法在很短的时间间隔内连续调用,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用。

函数节流的原理挺简单的,估计大家都想到了,那就是定时器。当我触发一个时间时,先setTimout让这个事件延迟一会再执行,如果在这个时间间隔内又触发了事件,那我们就clear掉原来的定时器,再setTimeout一个新的定时器延迟一会执行,就这样。

以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

1. window对象的resize、scroll事件

2. 拖拽时的mousemove事件

3. 射击游戏中的mousedown、keydown事件

4. 文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。

throttle 和 debounce 是解决请求和响应速度不匹配问题的两个方案。二者的差异在于选择不同的策略。

throttle 等时间 间隔执行函数。

debounce 时间间隔 t 内若再次触发事件,则重新计时,直到停止时间大于或等于 t 才执行函数。

一、throttle函数的简单实现

301_6167@:js;"> function throttle(fn,threshhold,scope) { threshhold || (threshhold = 250); var last,timer; return function () { var context = scope || this; var Now = +new Date(),args = arguments; if (last && Now - last + threshhold < 0) { // hold on to it clearTimeout(deferTimer); timer = setTimeout(function () { last = Now; fn.apply(context,args); },threshhold); } else { last = Now; fn.apply(context,args); } };}

调用方法

301_6167@:js;"> $('body').on('mousemove',throttle(function (event) { console.log('tick'); },1000));

二、debounce函数的简单实现

301_6167@:js;"> function debounce(fn,delay) { var timer = null; return function () { var context = this,args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context,delay); };}

调用方法

301_6167@:js;"> $('input.username').keypress(debounce(function (event) { // do the Ajax request },250));

三、简单的封装实现

= wait ) { fn.apply( context,wait ); context = wait = null; } } }}/** * debounce * @param fn,wait */var debounce = function ( fn,wait ) { return throttle( fn,true ); }

小结:这两个方法适用于会重复触发的一些事件,如:mousemove,keydown,keyup,keypress,scroll等。 如果只绑定原生事件,不加以控制,会使得浏览器卡顿,用户体验差。为了提高js性能,建议在使用以上及类似事件的时候用函数节流或者函数去抖加以控制。

四、underscore v1.7.0相关的源码剖析 

                         

1. _.throttle函数

301_6167@:js;"> _.throttle = function(func,options) { var context,args,result; var timeout = null; // 定时器 var prevIoUs = 0; // 上次触发的时间 if (!options) options = {}; var later = function() { prevIoUs = options.leading === false ? 0 : _.Now(); timeout = null; result = func.apply(context,args); if (!timeout) context = args = null; }; return function() { var Now = _.Now(); // 第一次是否执行 if (!prevIoUs && options.leading === false) prevIoUs = Now; // 这里引入了一个remaining的概念:还剩多长时间执行事件 var remaining = wait - (Now - prevIoUs); context = this; args = arguments; // remaining <= 0 考虑到事件停止后重新触发或者 // 正好相差wait的时候,这些情况下,会立即触发事件 // remaining > wait 没有考虑到相应场景 // 因为Now-prevIoUs永远都是正值,且不为0,那么 // remaining就会一直比wait小,没有大于wait的情况 // 估计是保险起见吧,这种情况也是立即执行 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; };};

由上可见,underscore考虑了比较多的情况:options.leading:

第一次是否执行,默认为true,表示第一次会执行,传入{leading:false}则禁用第一次执行options.trailing:最后一次是否执行,默认为true,表示最后一次会执行,传入{trailing: false}表示最后一次不执行所谓第一次是否执行,是刚开始触发事件时,要不要先触发事件,如果要,则prevIoUs=0,remaining 为负值,则立即调用了函数所谓最后一次是否执行,是事件结束后,最后一次触发了此方法,如果要执行,则设置定时器,即事件结束以后还要在执行一次。remianing > wait 表示客户端时间被修改过。

2. _.debounce函数

301_6167@:js;"> _.debounce = function(func,immediate) { // immediate默认为false var timeout,context,timestamp,result; var later = function() { // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func var last = _.Now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later,wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context,args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.Now(); // 第一次调用该方法时,且immediate为true,则调用func函数 var callNow = immediate && !timeout; // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数 if (!timeout) timeout = setTimeout(later,wait); if (callNow) { result = func.apply(context,args); context = args = null; } return result; };};

_.debounce实现的精彩之处我认为是通过递归启动计时器来代替通过调用clearTimeout来调整调用func函数的延时执行。

以上所述是小编给大家介绍的JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)。小编 jb51.cc 收集整理的教程希望能对你有所帮助,如果觉得小编不错,可分享给好友!感谢支持。

angular.js和vue.js中实现函数去抖(debounce)

angular.js和vue.js中实现函数去抖(debounce)

<h2>问题描述</h2> <p>搜索输入框中,只当用户停止输入后,才进行后续的操作,比如发起Http请求等。</p> <p>学过电子电路的同学应该知道按键防抖。原理是一样的:就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。本文将分别探讨在angular.js和vue.js中如何实现对用户输入的防抖。</p> <h2>angular.js中解决方案</h2> <p>把<a href="http://www.jqhtml.com/23816.html" target="_blank">函数防抖Debounce</a>写成一个service,方便多处调用:</p>

.factory(''debounce'', [''$timeout'',''$q'', function($timeout, $q) {
    // The service is actually this function, which we call with the func
    // that should be debounced and how long to wait in between calls
    return function debounce(func, wait, immediate) {
      var timeout;
      // Create a deferred object that will be resolved when we need to
      // actually call the func
      var deferred = $q.defer();
      return function() {
        var context = this, args = arguments;
        var later = function() {
          timeout = null;
          if(!immediate) {
            deferred.resolve(func.apply(context, args));
            deferred = $q.defer();
          }
        };
        var callNow = immediate &amp;&amp; !timeout;
        if ( timeout ) {
          $timeout.cancel(timeout);
        }
        timeout = $timeout(later, wait);
        if (callNow) {
          deferred.resolve(func.apply(context,args));
          deferred = $q.defer();
        }
        return deferred.promise;
      };
    };
  }])

<p>调用方法,在需要使用该功能的controller/directive中注入debounce,也要注入$watch,然后:</p>

$scope.$watch(''searchText'',debounce(function (newV, oldV) {
   console.log(newV, oldV);
   if (newV !== oldV) {
       $scope.getDatas(newV);
   }
}, 350));

<p>大功告成!</p> <h2>Vue.js中的解决方案</h2> <p>首先在公共函数文件中注册debounce</p>

export function debounce(func, delay) {
  let timer

  return function (...args) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() =&gt; {
      func.apply(this, args)
    }, delay)
  }
}

<p>然后在需要使用的组件中引入debounce,并且在created生命周期内调用:</p>

created() {
  this.$watch(''searchText'', debounce((newSearchText) =&gt; {
    this.getDatas(newSearchText)
  }, 200))
}

<p>大功告成!</p> <p>引用</p>

1. https://stackoverflow.com/questions/29771011/angularjs-watch-with-debounce
2. https://www.cnblogs.com/fsjohnhuang/p/4147810.html


原文地址:https://segmentfault.com/a/1190000012751237

JavaScript Debounce&Throttle

JavaScript Debounce&Throttle

前言

假设现在有个需求:监听滑动事件,并执行回调。
当你用触摸板或者鼠标滑动页面时,每秒钟大概会触发几十次scroll事件,而当你在手机
等移动终端上滑动页面时,每秒就会触发一百次scroll事件。如果我们的回调函数较为复杂,页面的性能就会变差。

解决问题的两种工具:debounce、throttle,它们有些类似,比如作用都是控制目标函数在一段时间内执行的次数;但更多的是不同:debounce使得在前后两次事件间隔不超过一定时间的情况下,无论触发多少次事件都只会执行一次回调函数。而throttle可以保证稳定的时间间隔执行一次回调函数。但需要弄清楚的是,无论是debounce还是throttle,控制的都是回调函数的执行,而不是事件的监听

另外,debounce和throttle都只是一种思想,可以有很多种实现,当然也可以自己去实现,后文中的代码都是基于lodash中的debounce和throttle。

debounce

想象这样一个场景:电梯即将关门,这时有个人上电梯,电梯就会停止关门。过了一会儿(间隔在电梯完全关上门所需要的时间之内),电梯又准备关门,又有人上电梯,又重复之前的步骤,直到最后一个人进来,电梯完全关上门,整个过程中电梯只关了一次门。

这个场景可以说是debounce在现实生活中的一个模型。回到代码层面:

// debounce(callback, millisecond, options)
var onScroll = debounce(animation, 1000, { leading: true, trailing: false });

// right
$(''#container'').addEventListener(''scroll'', onScroll);

// wrong
$(''#container'').addEventListener(''scroll'', function(){
    debounce(callback, millisecond, options);
});

debounce接受三个参数:要控制的函数、两次事件间隔的最大毫秒数、以及配置对象,返回一个函数,通常直接作为事件处理函数。详细说说第三个参数options,此参数默认值为:

{ leading: false,  trailing: true }

leading: 事件一被触发,先执行一次回调函数,再对之后的调用做控制。这样做的好处是事件一被触发,回调就执行,更真实;
trailing: 先对回调函数做控制,直到事件触发间隔超过设定时间,再调用回调函数,像上面电梯关门的例子
两者同时为true时,一次控制过程中回调会被执行两次;两者同时为false时,回调不执行。

常见的应用场景:拖拽窗口的大小、实时验证input

throttle

相比于debounce,throttle更像是一个特殊化的setInterval,就是说throttle包装过的函数会按固定的时间间隔执行,区别在于这个执行跟事件的触发有关,并且不用像setInterval那样手动取消。

throttle(callback, millisecond)

所以throttle更适用于需要不断执行但又需要控制执行次数来优化性能的函数,比如在滑动时根据滑动的数据(scrollTop等)不断改变某元素的样式。这种情况下,间隔时间设的过长就会不流畅,过短又起不到优化的效果。一般设为16ms,这样可以让帧率达到60fps,保持良好的视觉效果。说到这里就不得不提浏览器原生API requestAnimationFrame了。

粗略地说,requestAnimationFrame(callback)相当于

throttle(callback, 16);

rAF的优点在于它是原生的API,较为稳定。当然也有不少缺点,比如需要手动的启动和取消;浏览器tab不是active的时候不会被执行;不支持IE9;

caveat

  • 虽然说debounce、throttle有很多实现,甚至可以自己实现,但还是推荐直接使用loadash或者underscore,专业的工具库考虑到的事情往往比我们自己更多,不用担心为了使用两个函数而把整个lodash库都引入的问题,lodash包是可定制的,具体的方法自行Google。
  • 尽量将debounce或者throttle生成的函数直接作为事件处理函数,避免写出这种错误的代码:

    $(''#container'').addEventListener(''scroll'', function(){
        // 这里只是生成了函数,并没有执行,即使执行也无法达到控制的效果
       debounce(callback, millisecond, options);
    });
    
  • 使用变量保存debounce或者throttle返回的值后,可以调用取消的方法,就像setTimeout那样:

    onScroll = debounce(animation, 1000, { leading: true,             
    trailing: false });
    
    $(''#container'').addEventListener(''scroll'', onScroll);
    
    onScroll.cancel();
    

    参考文章: Debouncing and Throttling Explained Through Examples

JavaScript Throttle & Debounce

JavaScript Throttle & Debounce

Throttle

无视一定时间内所有的调用,适合在发生频度比较高的,处理比较重的时候使用。

var throttle = function (func, threshold, alt) {
    var last = Date.now();
    threshold = threshold || 100;

    return function () {
        var now = Date.now();

        if (now - last < threshold) {
            if (alt) {
                alt.apply(this, arguments);
            }
            return;
        }

        last = now;
        func.apply(this, arguments);
    };
};

Debounce

一定间隔内没有调用时,才开始执行被调用方法。

var debounce = function (func, threshold, execASAP) {
    var timeout = null;
    threshold = threshold || 100;

    return function () {
        var self = this;
        var args = arguments;
        var delayed = function () {
            if (!execASAP) {
                func.apply(self, args);
            }
            timeout = null;
        };

        if (timeout) {
            clearTimeout(timeout);
        } else if (execASAP) {
            func.apply(self, args);
        }

        timeout = setTimeout(delayed, threshold);
    };
};

Test

var test = function (wrapper, threshold) {
    var log = function () {
        console.log(Date.now() - start);
    };
    var wrapperedFunc = wrapper(log, threshold);
    var start = Date.now();
    var arr = [];

    for (var i = 0; i < 10; i++) {
        arr.push(wrapperedFunc);
    }

    while(i > 0) {
        var random = Math.random() * 1000;
        console.log(''index: '' + i);
        console.log(''random: '' + random);
        setTimeout(arr[--i], random);
    }
};

test(debounce, 1000);
test(throttle, 1000);

Javascript Throttle & Debounce应用介绍

Javascript Throttle & Debounce应用介绍

Throttle
无视一定时间内所有的调用,适合在发生频度比较高的,处理比较重的时候使用。
复制代码 代码如下:

var throttle = function (func, threshold, alt) {
var last = Date.now();
threshold = threshold || 100;
return function () {
var now = Date.now();
if (now - last < threshold) {
if (alt) {
alt.apply(this, arguments);
}
return;
}
last = now;
func.apply(this, arguments);
};
};

Debounce
一定间隔内没有调用时,才开始执行被调用方法。
复制代码 代码如下:

var debounce = function (func, threshold, execASAP) {
var timeout = null;
threshold = threshold || 100;
return function () {
var self = this;
var args = arguments;
var delayed = function () {
if (!execASAP) {
func.apply(self, args);
}
timeout = null;
};
if (timeout) {
clearTimeout(timeout);
} else if (execASAP) {
func.apply(self, args);
}
timeout = setTimeout(delayed, threshold);
};
};

Test
复制代码 代码如下:

var test = function (wrapper, threshold) {
var log = function () {
console.log(Date.now() - start);
};
var wrapperedFunc = wrapper(log, threshold);
var start = Date.now();
var arr = [];
for (var i = 0; i < 10; i++) {
arr.push(wrapperedFunc);
}
while(i > 0) {
var random = Math.random() * 1000;
console.log(''index: '' + i);
console.log(''random: '' + random);
setTimeout(arr[--i], random);
}
};
test(debounce, 1000);
test(throttle, 1000);
您可能感兴趣的文章:
  • javascript函数的节流[throttle]与防抖[debounce]
  • JavaScript中定时控制Throttle、Debounce和Immediate详解
  • JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)
  • 详解JavaScript节流函数中的Throttle
  • JavaScript 节流函数 Throttle 详解
  • javascript中的throttle和debounce浅析
  • Javascript节流函数throttle和防抖函数debounce

今天关于JavaScript性能优化之函数节流throttle与函数去抖的讲解已经结束,谢谢您的阅读,如果想了解更多关于angular.js和vue.js中实现函数去抖(debounce)、JavaScript Debounce&Throttle、JavaScript Throttle & Debounce、Javascript Throttle & Debounce应用介绍的相关知识,请在本站搜索。

本文标签: