如何在AngularJS中编写debounce服务

如何在AngularJS中编写debounce服务,angularjs,Angularjs,下划线库提供了一个反Bounce函数,可防止在设定的时间段内多次调用函数。他们的版本使用setTimeout 我们如何在纯AngularJS代码中做到这一点 此外,我们是否可以使用$q样式的承诺在去盎司周期后从被调用函数检索返回值?下面是此类服务的一个工作示例:。 它创建了一个$q延迟对象,该对象将在最终调用取消公告函数时解析 每次调用debounce函数时,都会返回对内部函数下一次调用的承诺 // Create an AngularJS service called debounce app.

下划线库提供了一个反Bounce函数,可防止在设定的时间段内多次调用函数。他们的版本使用setTimeout

我们如何在纯AngularJS代码中做到这一点


此外,我们是否可以使用$q样式的承诺在去盎司周期后从被调用函数检索返回值?

下面是此类服务的一个工作示例:。 它创建了一个
$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
呼叫被记录。

自从我写了上面的评论之后,我对这一点有了一些改变

简单的回答是,不需要对返回值的函数进行去抖动

为什么??好吧,从哲学上讲,我认为为事件而保持去Boung更有意义,而且只为事件而去Boung。如果你有一个方法返回一个你想去抖动的值,你应该去抖动导致你的方法向下游运行的事件。

用法:

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

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

  • 如果需要更改调用方中的状态,则返回何时应发送使用javascript闭包的work()回调
  • 超时变量-超时变量不是问题吗?也许吧?假设有两条指令使用debounce-Signal,输入表单验证器,我相信工厂方法会崩溃
  • 我目前使用的是:

    我将工厂更改为服务,因此每个指令都会获得一个新的debounce实例,也就是超时变量的新实例。-我还没有遇到过这样的情况:1指令需要timeout才能成为timeout[]

    在我的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);
                    });
                }
            };
        }])
    

    debounce服务和指令都有很好的实现,可以在以下位置与任何ng模型一起工作:

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

    bower install ng-debounce
    

    如果您处理的是模型交互,那么angularjs#1.3.0.beta6中已经提供了对此的支持

    Angular 1.3标准配置为去盎司 值得一提的是,debounce内置了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} }" />
    
    
    
    例如,在这里,我们有一个500毫秒的去抖动用于击键,而没有去抖动用于模糊

    文档
    阅读此处的文档:

    旁注:您很可能会问这个问题,因为您有太多的请求,而您只想触发一个请求。在过去的3天里,我一直面临着这个问题,经过十几次重新构造代码和阅读文档的尝试,我在不强制设置超时的情况下实现了我想要的。我在这里概括一下,但看看你是否能以同样的方式处理你的问题。非常神秘的评论!我很想看看你有什么想法。我同意这不应该仅仅用来对付太多的观察者太频繁地开火。这实际上不是我的问题,而是一个被放在邮件列表中的问题。我认为它可能有用的地方是,由于用户的输入,比如在服务器上异步查找以自动完成输入框。您可能只希望在用户停止键入一段时间后才进行查找。出于好奇,您是否发现返回的承诺有任何用途?例如,在自动完成的情况下,最好有一个承诺来告诉我用户最终键入的内容,而不是每次击键的承诺。我开始觉得你可以逃避承诺,也可以逃避去Bouncing+回调,但不能两者兼而有之。事实上,这不是去Bouncing服务中的一个bug。它在每次调用时返回相同的承诺,直到超时完成。问题在于,添加消息(已记录)在每次调用时都会根据相同的承诺一次又一次地调用then。因此,当单个承诺解决时,大量的处理程序正在运行。下面是一个更好的演示,它跟踪承诺,并且每个承诺只添加一个处理程序:严格来说,每个解析都返回相同的承诺。因此,您可以在每次解决之间接受第一个、中间或最后一个承诺。最后一行有一个拼写错误:});应该是}]);除非我错过了什么。。。为我工作,谢谢你!另外,
    返回的
    需要在调用
    之前被调用。然后()
    我同意Roy的观点,这是一个很好的观点;我使用了这个解决方案,并使用了下划线的debounceIt,似乎您不需要$q依赖项here@AndreyKouznetsov同意-cp来自Pete BD版本,修改后的代码-感谢你的回答似乎是对工厂的误解。工厂返回服务,因此在Pete的回答中,timeout变量在服务内部,就像您的一样。@m59文档:“服务配方生成服务,就像值或工厂配方一样,但它通过使用新运算符调用构造函数来实现。”Pete BD version您会得到一个缓存值,如文档中所述。@LeblancMeneses您对这个问题的看法是正确的,但不是解决方案。这两种方法都产生单例,因此每次使用相同的实例。这些文档是指第一次创建服务的方式。从那时起,将使用相同的实例。您不能以这种方式取消按钮单击的抖动。假设我想单击一个按钮来增加模型上的某个值,每次单击都是+N,然后对模型进行去盎司保存。@VladimirPrudnik
    <input ng-model='person.address' ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }" />