Path: blob/trunk/third_party/closure/goog/async/conditionaldelay.js
2868 views
// Copyright 2008 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 Defines a class useful for handling functions that must be16* invoked later when some condition holds. Examples include deferred function17* calls that return a boolean flag whether it succedeed or not.18*19* Example:20*21* function deferred() {22* var succeeded = false;23* // ... custom code24* return succeeded;25* }26*27* var deferredCall = new goog.async.ConditionalDelay(deferred);28* deferredCall.onSuccess = function() {29* alert('Success: The deferred function has been successfully executed.');30* }31* deferredCall.onFailure = function() {32* alert('Failure: Time limit exceeded.');33* }34*35* // Call the deferred() every 100 msec until it returns true,36* // or 5 seconds pass.37* deferredCall.start(100, 5000);38*39* // Stop the deferred function call (does nothing if it's not active).40* deferredCall.stop();41*42*/434445goog.provide('goog.async.ConditionalDelay');4647goog.require('goog.Disposable');48goog.require('goog.async.Delay');49505152/**53* A ConditionalDelay object invokes the associated function after a specified54* interval delay and checks its return value. If the function returns55* {@code true} the conditional delay is cancelled and {@see #onSuccess}56* is called. Otherwise this object keeps to invoke the deferred function until57* either it returns {@code true} or the timeout is exceeded. In the latter case58* the {@see #onFailure} method will be called.59*60* The interval duration and timeout can be specified each time the delay is61* started. Calling start on an active delay will reset the timer.62*63* @param {function():boolean} listener Function to call when the delay64* completes. Should return a value that type-converts to {@code true} if65* the call succeeded and this delay should be stopped.66* @param {Object=} opt_handler The object scope to invoke the function in.67* @constructor68* @struct69* @extends {goog.Disposable}70*/71goog.async.ConditionalDelay = function(listener, opt_handler) {72goog.async.ConditionalDelay.base(this, 'constructor');7374/**75* The delay interval in milliseconds to between the calls to the callback.76* Note, that the callback may be invoked earlier than this interval if the77* timeout is exceeded.78* @private {number}79*/80this.interval_ = 0;8182/**83* The timeout timestamp until which the delay is to be executed.84* A negative value means no timeout.85* @private {number}86*/87this.runUntil_ = 0;8889/**90* True if the listener has been executed, and it returned {@code true}.91* @private {boolean}92*/93this.isDone_ = false;9495/**96* The function that will be invoked after a delay.97* @private {function():boolean}98*/99this.listener_ = listener;100101/**102* The object context to invoke the callback in.103* @private {Object|undefined}104*/105this.handler_ = opt_handler;106107/**108* The underlying goog.async.Delay delegate object.109* @private {goog.async.Delay}110*/111this.delay_ = new goog.async.Delay(112goog.bind(this.onTick_, this), 0 /*interval*/, this /*scope*/);113};114goog.inherits(goog.async.ConditionalDelay, goog.Disposable);115116117/** @override */118goog.async.ConditionalDelay.prototype.disposeInternal = function() {119this.delay_.dispose();120delete this.listener_;121delete this.handler_;122goog.async.ConditionalDelay.superClass_.disposeInternal.call(this);123};124125126/**127* Starts the delay timer. The provided listener function will be called128* repeatedly after the specified interval until the function returns129* {@code true} or the timeout is exceeded. Calling start on an active timer130* will stop the timer first.131* @param {number=} opt_interval The time interval between the function132* invocations (in milliseconds). Default is 0.133* @param {number=} opt_timeout The timeout interval (in milliseconds). Takes134* precedence over the {@code opt_interval}, i.e. if the timeout is less135* than the invocation interval, the function will be called when the136* timeout is exceeded. A negative value means no timeout. Default is 0.137*/138goog.async.ConditionalDelay.prototype.start = function(139opt_interval, opt_timeout) {140this.stop();141this.isDone_ = false;142143var timeout = opt_timeout || 0;144this.interval_ = Math.max(opt_interval || 0, 0);145this.runUntil_ = timeout < 0 ? -1 : (goog.now() + timeout);146this.delay_.start(147timeout < 0 ? this.interval_ : Math.min(this.interval_, timeout));148};149150151/**152* Stops the delay timer if it is active. No action is taken if the timer is not153* in use.154*/155goog.async.ConditionalDelay.prototype.stop = function() {156this.delay_.stop();157};158159160/**161* @return {boolean} True if the delay is currently active, false otherwise.162*/163goog.async.ConditionalDelay.prototype.isActive = function() {164return this.delay_.isActive();165};166167168/**169* @return {boolean} True if the listener has been executed and returned170* {@code true} since the last call to {@see #start}.171*/172goog.async.ConditionalDelay.prototype.isDone = function() {173return this.isDone_;174};175176177/**178* Called when the listener has been successfully executed and returned179* {@code true}. The {@see #isDone} method should return {@code true} by now.180* Designed for inheritance, should be overridden by subclasses or on the181* instances if they care.182*/183goog.async.ConditionalDelay.prototype.onSuccess = function() {184// Do nothing by default.185};186187188/**189* Called when this delayed call is cancelled because the timeout has been190* exceeded, and the listener has never returned {@code true}.191* Designed for inheritance, should be overridden by subclasses or on the192* instances if they care.193*/194goog.async.ConditionalDelay.prototype.onFailure = function() {195// Do nothing by default.196};197198199/**200* A callback function for the underlying {@code goog.async.Delay} object. When201* executed the listener function is called, and if it returns {@code true}202* the delay is stopped and the {@see #onSuccess} method is invoked.203* If the timeout is exceeded the delay is stopped and the204* {@see #onFailure} method is called.205* @private206*/207goog.async.ConditionalDelay.prototype.onTick_ = function() {208var successful = this.listener_.call(this.handler_);209if (successful) {210this.isDone_ = true;211this.onSuccess();212} else {213// Try to reschedule the task.214if (this.runUntil_ < 0) {215// No timeout.216this.delay_.start(this.interval_);217} else {218var timeLeft = this.runUntil_ - goog.now();219if (timeLeft <= 0) {220this.onFailure();221} else {222this.delay_.start(Math.min(this.interval_, timeLeft));223}224}225}226};227228229