如何在AngularJS中写一个反弹服务
下划线库提供了一个去抖动function,可以防止在设定的时间内多次调用某个函数。 他们的版本使用setTimeout。
我们怎么能在纯AngularJS代码中做到这一点?
而且,我们可以使用$ q风格的promise来在去抖动之后从被调用的函数中获取返回值吗?
这是一个这样的服务的工作示例: http : //plnkr.co/edit/fJwRER?p=preview 。 它创build一个$q
延迟对象,当最终调用debounced函数时,这个对象将被parsing。
每次调用debounce函数时,都会返回对内部函数的下一个调用的承诺。
// Create an AngularJS service called debounce app.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 && !timeout; if ( timeout ) { $timeout.cancel(timeout); } timeout = $timeout(later, wait); if (callNow) { deferred.resolve(func.apply(context,args)); deferred = $q.defer(); } return deferred.promise; }; }; }]);
您可以通过使用promise上的then方法来获得去抖函数的返回值。
$scope.addMsg = function(msg) { console.log('addMsg called with', msg); return msg; }; $scope.addMsgDebounced = debounce($scope.addMsg, 2000, false); $scope.logReturn = function(msg) { console.log('logReturn called with', msg); var promise = $scope.addMsgDebounced(msg); promise.then(function(msg) { console.log('Promise resolved with', msg); }); };
如果快速连续多次调用logReturn
,则会看到logReturn
调用一遍又一遍logging,但只logging一次addMsg
调用。
angular1.3有标准的去抖动
值得一提的是,在Angular 1.3中内置了反弹技术。 正如你所期望的那样,它是作为一个指令来实现的。 你可以这样做:
<input ng-model='address' ng-model-options="{ debounce: 500 }" />
$ scope.address属性直到最后一次击键后500毫秒才会更新。
如果你需要更多的控制
如果你想要更多的粒度,你可以为不同的事件设置不同的跳动时间:
<input ng-model='person.address' ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }" />
在这里,例如,我们有一个500ms的反弹击键,并没有反弹模糊。
文档
阅读这里的文档: https : //docs.angularjs.org/api/ng/directive/ngModelOptions
自从我写了上面的评论之后,我对此有了一些改变。
简单的答案是,你不应该需要去除返回值的函数。
为什么? 那么从哲学angular度来说,我认为更有意义的是不要为事件而且只为了事件而发动反弹。 如果你有一个方法返回一个你想要去除的值,你应该去掉导致你的方法在下游运行的事件 。
皮特BD给了反弹服务很好的开始,但是我看到两个问题:
- 如果您需要更改调用方中的状态,则会返回使用JavaScript闭包的工作()callback。
- 超时variables – 是不是超时variables的问题? 超时[]也许? 想象2个使用debounce-signalr的指令,input表单validation器,
我相信工厂方法会崩溃。
我目前使用的是:
我将工厂更改为一个服务,所以每个指令都会得到一个新的debounce实例,即超时variables的新实例。 – 我没有遇到一个情况,其中1个指令将需要暂停超时[]。
.service('reactService', ['$timeout', '$q', function ($timeout, $q) { this.Debounce = function () { var timeout; this.Invoke = function (func, wait, immediate) { var context = this, args = arguments; var later = function () { timeout = null; if (!immediate) { func.apply(context, args); } }; var callNow = immediate && !timeout; if (timeout) { $timeout.cancel(timeout); } timeout = $timeout(later, wait); if (callNow) { func.apply(context, args); } }; return this; } }]);
在我的angularjs远程validation程序
.directive('remoteValidator', ['$http', 'reactService', function ($http, reactService) { return { require: 'ngModel', link: function (scope, elm, attrs, ctrl) { var newDebounce = new reactService.Debounce(); var work = function(){ //.... }; elm.on('blur keyup change', function () { newDebounce.Invoke(function(){ scope.$apply(work); }, 1000, false); }); } }; }])
有一个很好的执行debounce服务和指令,可以与任何ng模型在https://github.com/shahata/angular-debounce
或者只需使用以下命令安装
bower install ng-debounce
capaj/ng-tools/blob/master/src/debounce.html
用法:
app.directive('autosavable', function(debounce) { return { restrict : 'A', require : '?ngModel', link : function(scope, element, attrs, ngModel) { var debounced = debounce(function() { scope.$broadcast('autoSave'); }, 5000, false); element.bind('keypress', function(e) { debounced(); }); } }; });
如果您正在处理模型交互,对此的支持已经落在了angularjs#1.3.0.beta6中。