rt-sim-training-client/src/utils/throttle.js

171 lines
4.3 KiB
JavaScript
Raw Normal View History

2019-07-02 16:29:52 +08:00
/*
* 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.
*/
2019-08-02 14:31:29 +08:00
var ORIGIN_METHOD = '\0__throttleOriginMethod';
var RATE = '\0__throttleRate';
var THROTTLE_TYPE = '\0__throttleType';
2019-07-02 16:29:52 +08:00
/**
* @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) {
2019-08-02 14:31:29 +08:00
var currCall;
var lastCall = 0;
var lastExec = 0;
var timer = null;
var diff;
var scope;
var args;
var debounceNextCall;
2019-07-02 16:29:52 +08:00
2019-08-02 14:31:29 +08:00
delay = delay || 0;
2019-07-02 16:29:52 +08:00
function exec() {
2019-08-02 14:31:29 +08:00
lastExec = (new Date()).getTime();
timer = null;
fn.apply(scope, args || []);
2019-07-02 16:29:52 +08:00
}
var cb = function() {
2019-08-02 14:31:29 +08:00
currCall = (new Date()).getTime();
scope = this;
args = arguments;
var thisDelay = debounceNextCall || delay;
var thisDebounce = debounceNextCall || debounce;
debounceNextCall = null;
diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
2019-07-02 16:29:52 +08:00
2019-08-02 14:31:29 +08:00
clearTimeout(timer);
2019-07-02 16:29:52 +08:00
// 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) {
2019-08-02 14:31:29 +08:00
timer = setTimeout(exec, thisDelay);
2019-07-02 16:29:52 +08:00
} else {
if (diff >= 0) {
2019-08-02 14:31:29 +08:00
exec();
2019-07-02 16:29:52 +08:00
} else {
2019-08-02 14:31:29 +08:00
timer = setTimeout(exec, -diff);
2019-07-02 16:29:52 +08:00
}
}
2019-08-02 14:31:29 +08:00
lastCall = currCall;
};
2019-07-02 16:29:52 +08:00
/**
* Clear throttle.
* @public
*/
cb.clear = function() {
if (timer) {
2019-08-02 14:31:29 +08:00
clearTimeout(timer);
timer = null;
2019-07-02 16:29:52 +08:00
}
2019-08-02 14:31:29 +08:00
};
2019-07-02 16:29:52 +08:00
/**
* Enable debounce once.
*/
cb.debounceNextCall = function(debounceDelay) {
2019-08-02 14:31:29 +08:00
debounceNextCall = debounceDelay;
};
2019-07-02 16:29:52 +08:00
2019-08-02 14:31:29 +08:00
return cb;
2019-07-02 16:29:52 +08:00
}
/**
* 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) {
2019-08-02 14:31:29 +08:00
var fn = obj[fnAttr];
2019-07-02 16:29:52 +08:00
if (!fn) {
2019-08-02 14:31:29 +08:00
return;
2019-07-02 16:29:52 +08:00
}
2019-08-02 14:31:29 +08:00
var originFn = fn[ORIGIN_METHOD] || fn;
var lastThrottleType = fn[THROTTLE_TYPE];
var lastRate = fn[RATE];
2019-07-02 16:29:52 +08:00
if (lastRate !== rate || lastThrottleType !== throttleType) {
if (rate == null || !throttleType) {
2019-08-02 14:31:29 +08:00
return (obj[fnAttr] = originFn);
2019-07-02 16:29:52 +08:00
}
fn = obj[fnAttr] = throttle(
originFn, rate, throttleType === 'debounce'
2019-08-02 14:31:29 +08:00
);
fn[ORIGIN_METHOD] = originFn;
fn[THROTTLE_TYPE] = throttleType;
fn[RATE] = rate;
2019-07-02 16:29:52 +08:00
}
2019-08-02 14:31:29 +08:00
return fn;
2019-07-02 16:29:52 +08:00
}
/**
* Clear throttle. Example see throttle.createOrUpdate.
*
* @public
* @param {Object} obj
* @param {string} fnAttr
*/
export function clear(obj, fnAttr) {
2019-08-02 14:31:29 +08:00
var fn = obj[fnAttr];
2019-07-02 16:29:52 +08:00
if (fn && fn[ORIGIN_METHOD]) {
2019-08-02 14:31:29 +08:00
obj[fnAttr] = fn[ORIGIN_METHOD];
2019-07-02 16:29:52 +08:00
}
}