アンダースコアライブラリは、設定された期間内に関数への複数の呼び出しを防ぐデバウンス関数を提供します。それらのバージョンはsetTimeoutを利用します。
純粋なAngularJSコードでこれをどのように行うことができますか?
さらに、$ qスタイルのpromiseを使用して、デバウンス期間後に呼び出された関数から戻り値を取得できますか?
アンダースコアライブラリは、設定された期間内に関数への複数の呼び出しを防ぐデバウンス関数を提供します。それらのバージョンはsetTimeoutを利用します。
純粋なAngularJSコードでこれをどのように行うことができますか?
さらに、$ qスタイルのpromiseを使用して、デバウンス期間後に呼び出された関数から戻り値を取得できますか?
このようなサービスの実例を次に示します: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
記録されます。
上記のコメントを書いて以来、私はこれについて少し心変わりしました。
簡単に言えば、値を返す関数をデバウンスする必要はないということです。
なんで?まあ、哲学的には、イベントに対してのみデバウンスを続ける方が理にかなっていると思います。デバウンスしたい値を返すメソッドがある場合は、代わりに、メソッドをダウンストリームで実行させるイベントをデバウンスする必要があります。
Pete BD はデバウンス サービスを開始しましたが、次の 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);
});
}
};
}])
モデルの相互作用を扱っている場合、これのサポートは angularjs#1.3.0.beta6 に上陸しました。
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();
});
}
};
});