1 // Copyright 2006 The Closure Library Authors. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
16 * @fileoverview A timer class to which other classes and objects can
17 * listen on. This is only an abstraction above setInterval.
19 * @see ../demos/timers.html
22 goog.provide('goog.Timer');
24 goog.require('goog.events.EventTarget');
29 * Class for handling timing events.
31 * @param {number=} opt_interval Number of ms between ticks (Default: 1ms).
32 * @param {Object=} opt_timerObject An object that has setTimeout, setInterval,
33 * clearTimeout and clearInterval (eg Window).
35 * @extends {goog.events.EventTarget}
37 goog.Timer = function(opt_interval, opt_timerObject) {
38 goog.events.EventTarget.call(this);
41 * Number of ms between ticks
45 this.interval_ = opt_interval || 1;
48 * An object that implements setTimeout, setInterval, clearTimeout and
49 * clearInterval. We default to the window object. Changing this on
50 * goog.Timer.prototype changes the object for all timer instances which can
51 * be useful if your environment has some other implementation of timers than
56 this.timerObject_ = opt_timerObject || goog.Timer.defaultTimerObject;
59 * Cached tick_ bound to the object for later use in the timer.
63 this.boundTick_ = goog.bind(this.tick_, this);
66 * Firefox browser often fires the timer event sooner
67 * (sometimes MUCH sooner) than the requested timeout. So we
68 * compare the time to when the event was last fired, and
69 * reschedule if appropriate. See also goog.Timer.intervalScale
73 this.last_ = goog.now();
75 goog.inherits(goog.Timer, goog.events.EventTarget);
79 * Maximum timeout value.
81 * Timeout values too big to fit into a signed 32-bit integer may cause
82 * overflow in FF, Safari, and Chrome, resulting in the timeout being
83 * scheduled immediately. It makes more sense simply not to schedule these
84 * timeouts, since 24.8 days is beyond a reasonable expectation for the
85 * browser to stay open.
90 goog.Timer.MAX_TIMEOUT_ = 2147483647;
94 * Whether this timer is enabled
97 goog.Timer.prototype.enabled = false;
101 * An object that implements setTimout, setInterval, clearTimeout and
102 * clearInterval. We default to the global object. Changing
103 * goog.Timer.defaultTimerObject changes the object for all timer instances
104 * which can be useful if your environment has some other implementation of
105 * timers you'd like to use.
108 goog.Timer.defaultTimerObject = goog.global;
112 * A variable that controls the timer error correction. If the
113 * timer is called before the requested interval times
114 * intervalScale, which often happens on mozilla, the timer is
115 * rescheduled. See also this.last_
118 goog.Timer.intervalScale = 0.8;
122 * Variable for storing the result of setInterval
126 goog.Timer.prototype.timer_ = null;
130 * Gets the interval of the timer.
131 * @return {number} interval Number of ms between ticks.
133 goog.Timer.prototype.getInterval = function() {
134 return this.interval_;
139 * Sets the interval of the timer.
140 * @param {number} interval Number of ms between ticks.
142 goog.Timer.prototype.setInterval = function(interval) {
143 this.interval_ = interval;
144 if (this.timer_ && this.enabled) {
145 // Stop and then start the timer to reset the interval.
148 } else if (this.timer_) {
155 * Callback for the setTimeout used by the timer
158 goog.Timer.prototype.tick_ = function() {
160 var elapsed = goog.now() - this.last_;
162 elapsed < this.interval_ * goog.Timer.intervalScale) {
163 this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
164 this.interval_ - elapsed);
168 // Prevents setInterval from registering a duplicate timeout when called
169 // in the timer event handler.
171 this.timerObject_.clearTimeout(this.timer_);
176 // The timer could be stopped in the timer event handler.
178 this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
180 this.last_ = goog.now();
187 * Dispatches the TICK event. This is its own method so subclasses can override.
189 goog.Timer.prototype.dispatchTick = function() {
190 this.dispatchEvent(goog.Timer.TICK);
197 goog.Timer.prototype.start = function() {
200 // If there is no interval already registered, start it now
203 // window.setInterval in FireFox has a bug - it fires based on
204 // absolute time, rather than on relative time. What this means
205 // is that if a computer is sleeping/hibernating for 24 hours
206 // and the timer interval was configured to fire every 1000ms,
207 // then after the PC wakes up the timer will fire, in rapid
208 // succession, 3600*24 times.
209 // This bug is described here and is already fixed, but it will
210 // take time to propagate, so for now I am switching this over
211 // to setTimeout logic.
212 // https://bugzilla.mozilla.org/show_bug.cgi?id=376643
214 this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
216 this.last_ = goog.now();
224 goog.Timer.prototype.stop = function() {
225 this.enabled = false;
227 this.timerObject_.clearTimeout(this.timer_);
234 goog.Timer.prototype.disposeInternal = function() {
235 goog.Timer.superClass_.disposeInternal.call(this);
237 delete this.timerObject_;
242 * Constant for the timer's event type
245 goog.Timer.TICK = 'tick';
249 * Calls the given function once, after the optional pause.
251 * The function is always called asynchronously, even if the delay is 0. This
252 * is a common trick to schedule a function to run after a batch of browser
255 * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function
256 * or object that has a handleEvent method.
257 * @param {number=} opt_delay Milliseconds to wait; default is 0.
258 * @param {SCOPE=} opt_handler Object in whose scope to call the listener.
259 * @return {number} A handle to the timer ID.
262 goog.Timer.callOnce = function(listener, opt_delay, opt_handler) {
263 if (goog.isFunction(listener)) {
265 listener = goog.bind(listener, opt_handler);
267 } else if (listener && typeof listener.handleEvent == 'function') {
268 // using typeof to prevent strict js warning
269 listener = goog.bind(listener.handleEvent, listener);
271 throw Error('Invalid listener argument');
274 if (opt_delay > goog.Timer.MAX_TIMEOUT_) {
275 // Timeouts greater than MAX_INT return immediately due to integer
276 // overflow in many browsers. Since MAX_INT is 24.8 days, just don't
277 // schedule anything at all.
280 return goog.Timer.defaultTimerObject.setTimeout(
281 listener, opt_delay || 0);
287 * Clears a timeout initiated by callOnce
288 * @param {?number} timerId a timer ID.
290 goog.Timer.clear = function(timerId) {
291 goog.Timer.defaultTimerObject.clearTimeout(timerId);