Path: blob/trunk/third_party/closure/goog/async/debouncer.js
2868 views
// Copyright 2015 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Definition of the goog.async.Debouncer class.16*17* @see ../demos/timers.html18*/1920goog.provide('goog.async.Debouncer');2122goog.require('goog.Disposable');23goog.require('goog.Timer');24252627/**28* Debouncer will perform a specified action exactly once for any sequence of29* signals fired repeatedly so long as they are fired less than a specified30* interval apart (in milliseconds). Whether it receives one signal or multiple,31* it will always wait until a full interval has elapsed since the last signal32* before performing the action.33* @param {function(this: T, ...?)} listener Function to callback when the34* action is triggered.35* @param {number} interval Interval over which to debounce. The listener will36* only be called after the full interval has elapsed since the last signal.37* @param {T=} opt_handler Object in whose scope to call the listener.38* @constructor39* @struct40* @extends {goog.Disposable}41* @final42* @template T43*/44goog.async.Debouncer = function(listener, interval, opt_handler) {45goog.async.Debouncer.base(this, 'constructor');4647/**48* Function to callback49* @const @private {function(this: T, ...?)}50*/51this.listener_ =52opt_handler != null ? goog.bind(listener, opt_handler) : listener;5354/**55* Interval for the debounce time56* @const @private {number}57*/58this.interval_ = interval;5960/**61* Cached callback function invoked after the debounce timeout completes62* @const @private {!Function}63*/64this.callback_ = goog.bind(this.onTimer_, this);6566/**67* Indicates that the action is pending and needs to be fired.68* @private {boolean}69*/70this.shouldFire_ = false;7172/**73* Indicates the count of nested pauses currently in effect on the debouncer.74* When this count is not zero, fired actions will be postponed until the75* debouncer is resumed enough times to drop the pause count to zero.76* @private {number}77*/78this.pauseCount_ = 0;7980/**81* Timer for scheduling the next callback82* @private {?number}83*/84this.timer_ = null;8586/**87* When set this is a timestamp. On the onfire we want to reschedule the88* callback so it ends up at this time.89* @private {?number}90*/91this.refireAt_ = null;9293/**94* The last arguments passed into {@code fire}.95* @private {!IArrayLike}96*/97this.args_ = [];98};99goog.inherits(goog.async.Debouncer, goog.Disposable);100101102/**103* Notifies the debouncer that the action has happened. It will debounce the104* call so that the callback is only called after the last action in a sequence105* of actions separated by periods less the interval parameter passed to the106* constructor, passing the arguments from the last call of this function into107* the debounced function.108* @param {...?} var_args Arguments to pass on to the debounced function.109*/110goog.async.Debouncer.prototype.fire = function(var_args) {111this.args_ = arguments;112// When this method is called, we need to prevent fire() calls from within the113// previous interval from calling the callback. The simplest way of doing this114// is to call this.stop() which calls clearTimeout, and then reschedule the115// timeout. However clearTimeout and setTimeout are expensive, so we just116// leave them untouched and when they do happen we potentially reschedule.117this.shouldFire_ = false;118if (this.timer_) {119this.refireAt_ = goog.now() + this.interval_;120return;121}122this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_);123};124125126/**127* Cancels any pending action callback. The debouncer can be restarted by128* calling {@link #fire}.129*/130goog.async.Debouncer.prototype.stop = function() {131if (this.timer_) {132goog.Timer.clear(this.timer_);133this.timer_ = null;134}135this.refireAt_ = null;136this.shouldFire_ = false;137this.args_ = [];138};139140141/**142* Pauses the debouncer. All pending and future action callbacks will be delayed143* until the debouncer is resumed. Pauses can be nested.144*/145goog.async.Debouncer.prototype.pause = function() {146++this.pauseCount_;147};148149150/**151* Resumes the debouncer. If doing so drops the pausing count to zero, pending152* action callbacks will be executed as soon as possible, but still no sooner153* than an interval's delay after the previous call. Future action callbacks154* will be executed as normal.155*/156goog.async.Debouncer.prototype.resume = function() {157if (!this.pauseCount_) {158return;159}160161--this.pauseCount_;162if (!this.pauseCount_ && this.shouldFire_) {163this.doAction_();164}165};166167168/** @override */169goog.async.Debouncer.prototype.disposeInternal = function() {170this.stop();171goog.async.Debouncer.base(this, 'disposeInternal');172};173174175/**176* Handler for the timer to fire the debouncer.177* @private178*/179goog.async.Debouncer.prototype.onTimer_ = function() {180// There is a newer call to fire() within the debounce interval.181// Reschedule the callback and return.182if (this.refireAt_) {183this.timer_ =184goog.Timer.callOnce(this.callback_, this.refireAt_ - goog.now());185this.refireAt_ = null;186return;187}188this.timer_ = null;189190if (!this.pauseCount_) {191this.doAction_();192} else {193this.shouldFire_ = true;194}195};196197198/**199* Calls the callback.200* @private201*/202goog.async.Debouncer.prototype.doAction_ = function() {203this.shouldFire_ = false;204this.listener_.apply(null, this.args_);205};206207208