49

アンダースコアライブラリは、設定された期間内に関数への複数の呼び出しを防ぐデバウンス関数を提供します。それらのバージョンはsetTimeoutを利用します。

純粋なAngularJSコードでこれをどのように行うことができますか?

さらに、$ qスタイルのpromiseを使用して、デバウンス期間後に呼び出された関数から戻り値を取得できますか?

4

7 に答える 7

93

このようなサービスの実例を次に示します:http://plnkr.co/edit/fJwRER?p=preview$qこれは、デバウンスされた関数が最終的に呼び出されたときに解決される遅延オブジェクトを作成します。

関数が呼び出されるたびdebounceに、内部関数の次の呼び出しへのpromiseが返されます。

// 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記録されますが、1つの電話だけがaddMsg記録されます。

于 2012-11-10T06:50:28.387 に答える
33

上記のコメントを書いて以来、私はこれについて少し心変わりしました。

簡単に言えば、値を返す関数をデバウンスする必要はないということです。

なんで?まあ、哲学的には、イベントに対してのみデバウンスを続ける方が理にかなっていると思います。デバウンスしたい値を返すメソッドがある場合は、代わりに、メソッドをダウンストリームで実行させるイベントをデバウンスする必要があります。

于 2013-08-09T21:14:00.030 に答える
8

Pete BD はデバウンス サービスを開始しましたが、次の 2 つの問題があります。

  1. 呼び出し元で状態を変更する必要がある場合に、javascript クロージャーを使用する work() コールバックを送信する必要があるときに戻ります。
  2. タイムアウト変数 - そのタイムアウト変数は問題ではありませんか? タイムアウト[]多分?デバウンスを使用する2つのディレクティブを想像してください-シグナル、入力フォームバリデーター、ファクトリーアプローチは崩壊すると思います。

私が現在使用しているもの:

工場をサービスに変更して、各ディレクティブがタイムアウト変数の新しいインスタンスとも呼ばれるデバウンスの新しいインスタンスを取得するようにしました。- 1 つのディレクティブが timeout[] になるためにタイムアウトが必要になる状況に遭遇したことはありません。

.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);
                });
            }
        };
    }])
于 2014-02-26T23:42:10.247 に答える
1

モデルの相互作用を扱っている場合、これのサポートは angularjs#1.3.0.beta6 に上陸しました。

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

于 2014-04-28T13:42:08.370 に答える
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();
            });
        }
    };
});
于 2013-10-25T09:58:17.893 に答える