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

Pet*_* BD 45 angularjs

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

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

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

Pet*_* BD 90

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

每次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;
    };
  };
}]);
Run Code Online (Sandbox Code Playgroud)

您可以通过在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);
    });
};
Run Code Online (Sandbox Code Playgroud)

如果您logReturn快速连续多次呼叫,您将看到logReturn一次addMsg又一次地记录呼叫,但只记录了一次呼叫.

  • 这太棒了.它应该是核心代码的一部分. (9认同)
  • 实际上这不是辩论服务中的错误.它会在每次调用时返回相同的promise,直到超时完成.麻烦的是添加消息(已记录)在每次调用时一遍又一遍地对同一个承诺进行调用.因此,当单一承诺解决时,众多处理程序正在运行.这是一个跟踪承诺的更好的演示,每个承诺只添加一个处理程序:http://plnkr.co/edit/afX9v0?p = preview (3认同)
  • 在最后一行有一个错字:}); 应该 }]); 除非我错过了什么......为我工作,谢谢你! (2认同)
  • 另外,在调用`.then()`之前需要调用`returned` (2认同)

sup*_*ary 50

Angular 1.3作为标准进行了去抖动

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

<input ng-model='address' ng-model-options="{ debounce: 500 }" />
Run Code Online (Sandbox Code Playgroud)

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

如果你需要更多的控制权

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

<input ng-model='person.address' ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }" />
Run Code Online (Sandbox Code Playgroud)

例如,我们对按键进行500毫秒去抖动,对模糊没有去抖动.

文档

阅读此处的文档:https://docs.angularjs.org/api/ng/directive/ngModelOptions

  • 您不能以这种方式消除按钮点击。假设我想单击一个按钮来增加模型上的一些值,每次单击都是+N,并且去抖保存模型。 (2认同)
  • @VladimirPrudnikov - 是的,ng-model-options 仅适用于 ng-model,不适用于 ng-click。 (2认同)

Roy*_*ove 31

自从我写完上面的评论后,我对此有了一点改变.

简短的回答是,你不应该去抖动返回值的函数.

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

  • 我同意罗伊的观点 (3认同)
  • 这是一个非常好的观点; 我选择了这个解决方案并使用了下划线的去抖 (2认同)

Leb*_*ses 7

Pete BD为去抖服务提供了一个良好的开端,但是,我看到两个问题:

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

我目前使用的是:

我将工厂更改为服务,因此每个指令都获得了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;
    }
}]);
Run Code Online (Sandbox Code Playgroud)

在我的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);
                });
            }
        };
    }])
Run Code Online (Sandbox Code Playgroud)