You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							193 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
	
	
							193 lines
						
					
					
						
							5.3 KiB
						
					
					
				
 | 
						|
/* | 
						|
* Licensed to the Apache Software Foundation (ASF) under one | 
						|
* or more contributor license agreements.  See the NOTICE file | 
						|
* distributed with this work for additional information | 
						|
* regarding copyright ownership.  The ASF licenses this file | 
						|
* to you under the Apache License, Version 2.0 (the | 
						|
* "License"); you may not use this file except in compliance | 
						|
* with the License.  You may obtain a copy of the License at | 
						|
* | 
						|
*   http://www.apache.org/licenses/LICENSE-2.0 | 
						|
* | 
						|
* Unless required by applicable law or agreed to in writing, | 
						|
* software distributed under the License is distributed on an | 
						|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
						|
* KIND, either express or implied.  See the License for the | 
						|
* specific language governing permissions and limitations | 
						|
* under the License. | 
						|
*/ | 
						|
 | 
						|
/* | 
						|
* Licensed to the Apache Software Foundation (ASF) under one | 
						|
* or more contributor license agreements.  See the NOTICE file | 
						|
* distributed with this work for additional information | 
						|
* regarding copyright ownership.  The ASF licenses this file | 
						|
* to you under the Apache License, Version 2.0 (the | 
						|
* "License"); you may not use this file except in compliance | 
						|
* with the License.  You may obtain a copy of the License at | 
						|
* | 
						|
*   http://www.apache.org/licenses/LICENSE-2.0 | 
						|
* | 
						|
* Unless required by applicable law or agreed to in writing, | 
						|
* software distributed under the License is distributed on an | 
						|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
						|
* KIND, either express or implied.  See the License for the | 
						|
* specific language governing permissions and limitations | 
						|
* under the License. | 
						|
*/ | 
						|
var ORIGIN_METHOD = '\0__throttleOriginMethod'; | 
						|
var RATE = '\0__throttleRate'; | 
						|
var THROTTLE_TYPE = '\0__throttleType'; | 
						|
/** | 
						|
 * @public | 
						|
 * @param {(Function)} fn | 
						|
 * @param {number} [delay=0] Unit: ms. | 
						|
 * @param {boolean} [debounce=false] | 
						|
 *        true: If call interval less than `delay`, only the last call works. | 
						|
 *        false: If call interval less than `delay, call works on fixed rate. | 
						|
 * @return {(Function)} throttled fn. | 
						|
 */ | 
						|
 | 
						|
function throttle(fn, delay, debounce) { | 
						|
  var currCall; | 
						|
  var lastCall = 0; | 
						|
  var lastExec = 0; | 
						|
  var timer = null; | 
						|
  var diff; | 
						|
  var scope; | 
						|
  var args; | 
						|
  var debounceNextCall; | 
						|
  delay = delay || 0; | 
						|
 | 
						|
  function exec() { | 
						|
    lastExec = new Date().getTime(); | 
						|
    timer = null; | 
						|
    fn.apply(scope, args || []); | 
						|
  } | 
						|
 | 
						|
  var cb = function () { | 
						|
    currCall = new Date().getTime(); | 
						|
    scope = this; | 
						|
    args = arguments; | 
						|
    var thisDelay = debounceNextCall || delay; | 
						|
    var thisDebounce = debounceNextCall || debounce; | 
						|
    debounceNextCall = null; | 
						|
    diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay; | 
						|
    clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later | 
						|
    // than a new call of `cb`, that is, preserving the command order. Consider | 
						|
    // calculating "scale rate" when roaming as an example. When a call of `cb` | 
						|
    // happens, either the `exec` is called dierectly, or the call is delayed. | 
						|
    // But the delayed call should never be later than next call of `cb`. Under | 
						|
    // this assurance, we can simply update view state each time `dispatchAction` | 
						|
    // triggered by user roaming, but not need to add extra code to avoid the | 
						|
    // state being "rolled-back". | 
						|
 | 
						|
    if (thisDebounce) { | 
						|
      timer = setTimeout(exec, thisDelay); | 
						|
    } else { | 
						|
      if (diff >= 0) { | 
						|
        exec(); | 
						|
      } else { | 
						|
        timer = setTimeout(exec, -diff); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    lastCall = currCall; | 
						|
  }; | 
						|
  /** | 
						|
   * Clear throttle. | 
						|
   * @public | 
						|
   */ | 
						|
 | 
						|
 | 
						|
  cb.clear = function () { | 
						|
    if (timer) { | 
						|
      clearTimeout(timer); | 
						|
      timer = null; | 
						|
    } | 
						|
  }; | 
						|
  /** | 
						|
   * Enable debounce once. | 
						|
   */ | 
						|
 | 
						|
 | 
						|
  cb.debounceNextCall = function (debounceDelay) { | 
						|
    debounceNextCall = debounceDelay; | 
						|
  }; | 
						|
 | 
						|
  return cb; | 
						|
} | 
						|
/** | 
						|
 * Create throttle method or update throttle rate. | 
						|
 * | 
						|
 * @example | 
						|
 * ComponentView.prototype.render = function () { | 
						|
 *     ... | 
						|
 *     throttle.createOrUpdate( | 
						|
 *         this, | 
						|
 *         '_dispatchAction', | 
						|
 *         this.model.get('throttle'), | 
						|
 *         'fixRate' | 
						|
 *     ); | 
						|
 * }; | 
						|
 * ComponentView.prototype.remove = function () { | 
						|
 *     throttle.clear(this, '_dispatchAction'); | 
						|
 * }; | 
						|
 * ComponentView.prototype.dispose = function () { | 
						|
 *     throttle.clear(this, '_dispatchAction'); | 
						|
 * }; | 
						|
 * | 
						|
 * @public | 
						|
 * @param {Object} obj | 
						|
 * @param {string} fnAttr | 
						|
 * @param {number} [rate] | 
						|
 * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce' | 
						|
 * @return {Function} throttled function. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function createOrUpdate(obj, fnAttr, rate, throttleType) { | 
						|
  var fn = obj[fnAttr]; | 
						|
 | 
						|
  if (!fn) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var originFn = fn[ORIGIN_METHOD] || fn; | 
						|
  var lastThrottleType = fn[THROTTLE_TYPE]; | 
						|
  var lastRate = fn[RATE]; | 
						|
 | 
						|
  if (lastRate !== rate || lastThrottleType !== throttleType) { | 
						|
    if (rate == null || !throttleType) { | 
						|
      return obj[fnAttr] = originFn; | 
						|
    } | 
						|
 | 
						|
    fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce'); | 
						|
    fn[ORIGIN_METHOD] = originFn; | 
						|
    fn[THROTTLE_TYPE] = throttleType; | 
						|
    fn[RATE] = rate; | 
						|
  } | 
						|
 | 
						|
  return fn; | 
						|
} | 
						|
/** | 
						|
 * Clear throttle. Example see throttle.createOrUpdate. | 
						|
 * | 
						|
 * @public | 
						|
 * @param {Object} obj | 
						|
 * @param {string} fnAttr | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function clear(obj, fnAttr) { | 
						|
  var fn = obj[fnAttr]; | 
						|
 | 
						|
  if (fn && fn[ORIGIN_METHOD]) { | 
						|
    obj[fnAttr] = fn[ORIGIN_METHOD]; | 
						|
  } | 
						|
} | 
						|
 | 
						|
exports.throttle = throttle; | 
						|
exports.createOrUpdate = createOrUpdate; | 
						|
exports.clear = clear; |