| /* |
| * 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. |
| */ |
| |
| export 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. |
| */ |
| |
| export 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 |
| */ |
| |
| export function clear(obj, fnAttr) { |
| var fn = obj[fnAttr]; |
| |
| if (fn && fn[ORIGIN_METHOD]) { |
| obj[fnAttr] = fn[ORIGIN_METHOD]; |
| } |
| } |