throttle
我们这里说的throttle就是函数节流的意思。再说的通俗一点就是函数调用的频度控制器,是连续执行时间间隔控制。主要应用的场景比如:
- 鼠标移动,mousemove 事件
- DOM 元素动态定位,window对象的resize和scroll 事件
有人形象的把上面说的事件形象的比喻成机关枪的扫射,throttle就是机关枪的扳机,你不放扳机,它就一直扫射。我们开发时用的上面这些事件也是一样,你不松开鼠标,它的事件就一直触发。回到window resize和scroll事件的基本优化提到的优化:
setTimeout和clearTimeout其实就是一个简单的 throttle,很多好的控制了resize事件的调用频度。
debounce
debounce和throttle很像,debounce是空闲时间必须大于或等于 一定值的时候,才会执行调用方法。debounce是空闲时间的间隔控制。比如我们做autocomplete,这时需要我们很好的控制输入文字时调用方法时间间隔。一般时第一个输入的字符马上开始调用,根据一定的时间间隔重复调用执行的方法。对于变态的输入,比如按住某一个建不放的时候特别有用。
debounce主要应用的场景比如:
- 文本输入keydown 事件,keyup 事件,例如做autocomplete
这类网上的方法有很多,比如Underscore.js就对throttle和debounce进行封装
angular 1.3版本之后可以使用 ngModelOptions参数在设置相应的debounce
ngModelOptions | Object | options to apply to the current model. Valid keys are:
|
angular.module('lz.utils.debounce', [])
.service('$debounce', ['$timeout', function ($timeout) {
return function (func, wait, immediate, invokeApply) {
var timeout, args, me, result;
function debounce() {
/* jshint validthis:true */
me = this;
args = arguments;
var later = function () {
timeout = null;
if (!immediate) {
result = func.apply(me, args);
}
};
var callNow = immediate && !timeout;
if (timeout) {
$timeout.cancel(timeout);
}
timeout = $timeout(later, wait, invokeApply);
if (callNow) {
result = func.apply(me, args);
}
return result;
}
debounce.cancel = function () {
$timeout.cancel(timeout);
timeout = null;
};
return debounce;
};
}])
/**
* usage: <XX lz-debounce="500" immediate="true" ng-model="test"></XX>
*/
.directive('lzDebounce', ['$debounce', '$parse', function (debounce, $parse) {
return {
require: 'ngModel',
priority: 999,
link: function ($scope, $element, $attrs, ngModelController) {
var debounceDuration = $parse($attrs.debounce)($scope);
var immediate = !!$parse($attrs.immediate)($scope);
var debouncedValue, pass;
var prevRender = ngModelController.$render.bind(ngModelController);
var commitSoon = debounce(function (viewValue) {
pass = true;
ngModelController.$$lastCommittedViewValue = debouncedValue;
ngModelController.$setViewValue(viewValue);
pass = false;
}, parseInt(debounceDuration, 10), immediate);
ngModelController.$render = function () {
prevRender();
commitSoon.cancel();
//we must be first parser for this to work properly,
//so we have priority 999 so that we unshift into parsers last
debouncedValue = this.$viewValue;
};
ngModelController.$parsers.unshift(function (value) {
if (pass) {
debouncedValue = value;
return value;
} else {
commitSoon(ngModelController.$viewValue);
return debouncedValue;
}
});
}
};
}]);