Skip to content

Commit 6a5b074

Browse files
committed
perf(utils): extract lodash.throttle and lodash.debounce
1 parent a4c67e2 commit 6a5b074

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

Diff for: src/core/utils/lodash/debounce.ts

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* source by `lodash`
3+
* https://github.com/lodash/lodash.git
4+
*/
5+
/* istanbul ignore next */
6+
import { isObject } from '../index';
7+
8+
function debounce(
9+
func: (...args: any[]) => any,
10+
wait: number,
11+
options?: { leading?: boolean; trailing?: boolean; maxWait?: number },
12+
) {
13+
let lastArgs: any[] | undefined,
14+
lastThis: undefined,
15+
maxWait: number,
16+
result: any,
17+
timerId: number | undefined,
18+
lastCallTime: number | undefined;
19+
20+
let lastInvokeTime = 0;
21+
let leading = false;
22+
let maxing = false;
23+
let trailing = true;
24+
25+
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
26+
const useRAF =
27+
!wait && wait !== 0 && typeof window.requestAnimationFrame === 'function';
28+
29+
if (typeof func !== 'function') {
30+
throw new TypeError('Expected a function');
31+
}
32+
wait = +wait || 0;
33+
if (isObject(options)) {
34+
leading = !!options.leading;
35+
maxing = 'maxWait' in options;
36+
maxWait = maxing ? Math.max(+options.maxWait! || 0, wait) : maxWait!;
37+
trailing = 'trailing' in options ? !!options.trailing : trailing;
38+
}
39+
40+
function invokeFunc(time: number) {
41+
const args = lastArgs;
42+
const thisArg = lastThis;
43+
44+
lastArgs = lastThis = undefined;
45+
lastInvokeTime = time;
46+
result = func.apply(thisArg, args as any);
47+
return result;
48+
}
49+
50+
function startTimer(pendingFunc: () => void, wait: number) {
51+
if (useRAF) {
52+
window.cancelAnimationFrame(timerId!);
53+
return window.requestAnimationFrame(pendingFunc);
54+
}
55+
return setTimeout(pendingFunc, wait);
56+
}
57+
58+
function cancelTimer(id: number) {
59+
if (useRAF) {
60+
return window.cancelAnimationFrame(id);
61+
}
62+
clearTimeout(id);
63+
}
64+
65+
function leadingEdge(time: number) {
66+
// Reset any `maxWait` timer.
67+
lastInvokeTime = time;
68+
// Start the timer for the trailing edge.
69+
timerId = startTimer(timerExpired, wait);
70+
// Invoke the leading edge.
71+
return leading ? invokeFunc(time) : result;
72+
}
73+
74+
function remainingWait(time: number) {
75+
const timeSinceLastCall = time - lastCallTime!;
76+
const timeSinceLastInvoke = time - lastInvokeTime;
77+
const timeWaiting = wait - timeSinceLastCall;
78+
79+
return maxing
80+
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
81+
: timeWaiting;
82+
}
83+
84+
function shouldInvoke(time: number) {
85+
const timeSinceLastCall = time - lastCallTime!;
86+
const timeSinceLastInvoke = time - lastInvokeTime;
87+
88+
// Either this is the first call, activity has stopped and we're at the
89+
// trailing edge, the system time has gone backwards and we're treating
90+
// it as the trailing edge, or we've hit the `maxWait` limit.
91+
return (
92+
lastCallTime === undefined ||
93+
timeSinceLastCall >= wait ||
94+
timeSinceLastCall < 0 ||
95+
(maxing && timeSinceLastInvoke >= maxWait)
96+
);
97+
}
98+
99+
function timerExpired() {
100+
const time = Date.now();
101+
if (shouldInvoke(time)) {
102+
return trailingEdge(time);
103+
}
104+
// Restart the timer.
105+
timerId = startTimer(timerExpired, remainingWait(time));
106+
}
107+
108+
function trailingEdge(time: number) {
109+
timerId = undefined;
110+
111+
// Only invoke if we have `lastArgs` which means `func` has been
112+
// debounced at least once.
113+
if (trailing && lastArgs) {
114+
return invokeFunc(time);
115+
}
116+
lastArgs = lastThis = undefined;
117+
return result;
118+
}
119+
120+
function cancel() {
121+
if (timerId !== undefined) {
122+
cancelTimer(timerId);
123+
}
124+
lastInvokeTime = 0;
125+
lastArgs = lastCallTime = lastThis = timerId = undefined;
126+
}
127+
128+
function flush() {
129+
return timerId === undefined ? result : trailingEdge(Date.now());
130+
}
131+
132+
function pending() {
133+
return timerId !== undefined;
134+
}
135+
136+
function debounced(this: any, ...args: any[]) {
137+
const time = Date.now();
138+
const isInvoking = shouldInvoke(time);
139+
140+
lastArgs = args;
141+
lastThis = this;
142+
lastCallTime = time;
143+
144+
if (isInvoking) {
145+
if (timerId === undefined) {
146+
return leadingEdge(lastCallTime);
147+
}
148+
if (maxing) {
149+
// Handle invocations in a tight loop.
150+
timerId = startTimer(timerExpired, wait);
151+
return invokeFunc(lastCallTime);
152+
}
153+
}
154+
if (timerId === undefined) {
155+
timerId = startTimer(timerExpired, wait);
156+
}
157+
return result;
158+
}
159+
debounced.cancel = cancel;
160+
debounced.flush = flush;
161+
debounced.pending = pending;
162+
return debounced;
163+
}
164+
165+
export default debounce;

Diff for: src/core/utils/lodash/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* source by `lodash`
3+
* https://github.com/lodash/lodash.git
4+
*/
5+
/* istanbul ignore next */
6+
export { default as debounce } from './debounce';
7+
export { default as throttle } from './throttle';

Diff for: src/core/utils/lodash/throttle.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* source by `lodash`
3+
* https://github.com/lodash/lodash.git
4+
*/
5+
/* istanbul ignore next */
6+
import debounce from './debounce';
7+
import { isObject } from '../index';
8+
9+
function throttle(
10+
func: (...args: any[]) => any,
11+
wait: number,
12+
options?: { leading?: boolean; trailing?: boolean; maxWait?: number },
13+
) {
14+
let leading = true;
15+
let trailing = true;
16+
17+
if (typeof func !== 'function') {
18+
throw new TypeError('Expected a function');
19+
}
20+
if (isObject(options)) {
21+
leading = 'leading' in options ? !!options.leading : leading;
22+
trailing = 'trailing' in options ? !!options.trailing : trailing;
23+
}
24+
return debounce(func, wait, {
25+
leading,
26+
trailing,
27+
maxWait: wait,
28+
});
29+
}
30+
31+
export default throttle;

0 commit comments

Comments
 (0)