throttle.js
5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
define(function () {
var lib = {};
var ORIGIN_METHOD = '\0__throttleOriginMethod';
var RATE = '\0__throttleRate';
/**
* 频率控制 返回函数连续调用时,fn 执行频率限定为每多少时间执行一次
* 例如常见效果:
* notifyWhenChangesStop
* 频繁调用时,只保证最后一次执行
* 配成:trailing:true;debounce:true 即可
* notifyAtFixRate
* 频繁调用时,按规律心跳执行
* 配成:trailing:true;debounce:false 即可
* 注意:
* 根据model更新view的时候,可以使用throttle,
* 但是根据view更新model的时候,避免使用这种延迟更新的方式。
* 因为这可能导致model和server同步出现问题。
*
* @public
* @param {(Function|Array.<Function>)} fn 需要调用的函数
* 如果fn为array,则表示可以对多个函数进行throttle。
* 他们共享同一个timer。
* @param {number} delay 延迟时间,单位毫秒
* @param {bool} trailing 是否保证最后一次触发的执行
* true:表示保证最后一次调用会触发执行。
* 但任何调用后不可能立即执行,总会delay。
* false:表示不保证最后一次调用会触发执行。
* 但只要间隔大于delay,调用就会立即执行。
* @param {bool} debounce 节流
* true:表示:频繁调用(间隔小于delay)时,根本不执行
* false:表示:频繁调用(间隔小于delay)时,按规律心跳执行
* @return {(Function|Array.<Function>)} 实际调用函数。
* 当输入的fn为array时,返回值也为array。
* 每项是Function。
*/
lib.throttle = function (fn, delay, trailing, debounce) {
var currCall = (new Date()).getTime();
var lastCall = 0;
var lastExec = 0;
var timer = null;
var diff;
var scope;
var args;
var isSingle = typeof fn === 'function';
delay = delay || 0;
if (isSingle) {
return createCallback();
}
else {
var ret = [];
for (var i = 0; i < fn.length; i++) {
ret[i] = createCallback(i);
}
return ret;
}
function createCallback(index) {
function exec() {
lastExec = (new Date()).getTime();
timer = null;
(isSingle ? fn : fn[index]).apply(scope, args || []);
}
var cb = function () {
currCall = (new Date()).getTime();
scope = this;
args = arguments;
diff = currCall - (debounce ? lastCall : lastExec) - delay;
clearTimeout(timer);
if (debounce) {
if (trailing) {
timer = setTimeout(exec, delay);
}
else if (diff >= 0) {
exec();
}
}
else {
if (diff >= 0) {
exec();
}
else if (trailing) {
timer = setTimeout(exec, -diff);
}
}
lastCall = currCall;
};
/**
* Clear throttle.
* @public
*/
cb.clear = function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return cb;
}
};
/**
* 按一定频率执行,最后一次调用总归会执行
*
* @public
*/
lib.fixRate = function (fn, delay) {
return delay != null
? lib.throttle(fn, delay, true, false)
: fn;
};
/**
* 直到不频繁调用了才会执行,最后一次调用总归会执行
*
* @public
*/
lib.debounce = function (fn, delay) {
return delay != null
? lib.throttle(fn, delay, true, true)
: fn;
};
/**
* 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' or 'debounce'
*/
lib.createOrUpdate = function (obj, fnAttr, rate, throttleType) {
var fn = obj[fnAttr];
if (!fn || rate == null || !throttleType) {
return;
}
var originFn = fn[ORIGIN_METHOD] || fn;
var lastRate = fn[RATE];
if (lastRate !== rate) {
fn = obj[fnAttr] = lib[throttleType](originFn, rate);
fn[ORIGIN_METHOD] = originFn;
fn[RATE] = rate;
}
};
/**
* Clear throttle. Example see throttle.createOrUpdate.
*
* @public
* @param {Object} obj
* @param {string} fnAttr
*/
lib.clear = function (obj, fnAttr) {
var fn = obj[fnAttr];
if (fn && fn[ORIGIN_METHOD]) {
obj[fnAttr] = fn[ORIGIN_METHOD];
}
};
return lib;
});