如何在AngularJS中编写去抖动服务

时间:2012-11-10 06:50:28

标签: angularjs

下划线库提供去抖功能,可防止在设定的时间段内对函数进行多次调用。他们的版本使用了setTimeout。

我们怎样才能在纯AngularJS代码中执行此操作?

此外,我们可以利用$ q样式承诺在去抖期后从被调用函数中检索返回值吗?

7 个答案:

答案 0 :(得分:89)

以下是此类服务的工作示例:http://plnkr.co/edit/fJwRER?p=preview。 它会创建一个$q延迟对象,在最终调用去抖动函数时将对其进行解析。

每次调用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方法从debounced函数中获取返回值。

$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来电记录过来,但只记录了一次addMsg来电。

答案 1 :(得分:50)

Angular 1.3已作为标准去抖动

值得一提的是Debounce内置了Angular 1.3。正如您所期望的那样,它是作为指令实施的。你可以这样做:

<input ng-model='address' ng-model-options="{ debounce: 500 }" />

$ scope.address属性直到最后一次击键后500ms才更新。

如果您需要更多控制

如果您想要更多粒度,可以为不同的事件设置不同的跳出时间:

<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

答案 2 :(得分:31)

由于我已经写了上面的评论,我对此有了一些改变。

简短的回答是,您不需要去除返回值的函数。

为什么呢?好吧,从哲学的角度来看,我认为对事件进行辩解更有意义,而且只针对事件。如果你有一个方法可以返回一个你想去抖动的值,你应该去除导致你的方法向下游运行的事件

答案 3 :(得分:7)

Pete BD为辩护服务提供了一个良好的开端,但是,我发现了两个问题:

    如果你需要在调用者中更改状态,
  1. 返回应该发送使用javascript闭包的work()回调函数。
  2. 超时变量 - 是不是超时变量有问题?超时[]也许?想象使用去抖动的2个指令 - 信号器,输入形式验证器,我相信工厂方法会崩溃。
  3. 我目前使用的是:

    我将工厂更改为服务,因此每个指令都获得了debounce的新实例,即超时变量的新实例。 - 我没有遇到过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远程验证器

        .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);
                    });
                }
            };
        }])
    

答案 4 :(得分:2)

可以在以下任意ng模型中使用去抖服务和指令的良好实现:https://github.com/shahata/angular-debounce

或者只需使用以下方法安装:

bower install ng-debounce

答案 5 :(得分:1)

https://github.com/capaj/ng-tools/blob/master/src/debounce.js

用法:

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();
            });
        }
    };
});

答案 6 :(得分:1)

如果您正在处理模型交互,那么对此的支持已落在angularjs#1.3.0.beta6中。

https://docs.angularjs.org/api/ng/directive/ngModelOptions