root / HServer / 00.Server / 00.Program / node_modules / lodash / debounce.js
이력 | 보기 | 이력해설 | 다운로드 (5.93 KB)
1 | 39 | HKM | var isObject = require('./isObject'), |
---|---|---|---|
2 | now = require('./now'),
|
||
3 | toNumber = require('./toNumber');
|
||
4 | |||
5 | /** Error message constants. */
|
||
6 | var FUNC_ERROR_TEXT = 'Expected a function'; |
||
7 | |||
8 | /* Built-in method references for those with the same name as other `lodash` methods. */
|
||
9 | var nativeMax = Math.max,
|
||
10 | nativeMin = Math.min; |
||
11 | |||
12 | /**
|
||
13 | * Creates a debounced function that delays invoking `func` until after `wait`
|
||
14 | * milliseconds have elapsed since the last time the debounced function was
|
||
15 | * invoked. The debounced function comes with a `cancel` method to cancel
|
||
16 | * delayed `func` invocations and a `flush` method to immediately invoke them.
|
||
17 | * Provide `options` to indicate whether `func` should be invoked on the
|
||
18 | * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
|
||
19 | * with the last arguments provided to the debounced function. Subsequent
|
||
20 | * calls to the debounced function return the result of the last `func`
|
||
21 | * invocation.
|
||
22 | *
|
||
23 | * **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||
24 | * invoked on the trailing edge of the timeout only if the debounced function
|
||
25 | * is invoked more than once during the `wait` timeout.
|
||
26 | *
|
||
27 | * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||
28 | * until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||
29 | *
|
||
30 | * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||
31 | * for details over the differences between `_.debounce` and `_.throttle`.
|
||
32 | *
|
||
33 | * @static
|
||
34 | * @memberOf _
|
||
35 | * @since 0.1.0
|
||
36 | * @category Function
|
||
37 | * @param {Function} func The function to debounce.
|
||
38 | * @param {number} [wait=0] The number of milliseconds to delay.
|
||
39 | * @param {Object} [options={}] The options object.
|
||
40 | * @param {boolean} [options.leading=false]
|
||
41 | * Specify invoking on the leading edge of the timeout.
|
||
42 | * @param {number} [options.maxWait]
|
||
43 | * The maximum time `func` is allowed to be delayed before it's invoked.
|
||
44 | * @param {boolean} [options.trailing=true]
|
||
45 | * Specify invoking on the trailing edge of the timeout.
|
||
46 | * @returns {Function} Returns the new debounced function.
|
||
47 | * @example
|
||
48 | *
|
||
49 | * // Avoid costly calculations while the window size is in flux.
|
||
50 | * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
|
||
51 | *
|
||
52 | * // Invoke `sendMail` when clicked, debouncing subsequent calls.
|
||
53 | * jQuery(element).on('click', _.debounce(sendMail, 300, {
|
||
54 | * 'leading': true,
|
||
55 | * 'trailing': false
|
||
56 | * }));
|
||
57 | *
|
||
58 | * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
|
||
59 | * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
|
||
60 | * var source = new EventSource('/stream');
|
||
61 | * jQuery(source).on('message', debounced);
|
||
62 | *
|
||
63 | * // Cancel the trailing debounced invocation.
|
||
64 | * jQuery(window).on('popstate', debounced.cancel);
|
||
65 | */
|
||
66 | function debounce(func, wait, options) { |
||
67 | var lastArgs,
|
||
68 | lastThis, |
||
69 | maxWait, |
||
70 | result, |
||
71 | timerId, |
||
72 | lastCallTime, |
||
73 | lastInvokeTime = 0,
|
||
74 | leading = false,
|
||
75 | maxing = false,
|
||
76 | trailing = true;
|
||
77 | |||
78 | if (typeof func != 'function') { |
||
79 | throw new TypeError(FUNC_ERROR_TEXT); |
||
80 | } |
||
81 | wait = toNumber(wait) || 0;
|
||
82 | if (isObject(options)) {
|
||
83 | leading = !!options.leading; |
||
84 | maxing = 'maxWait' in options; |
||
85 | maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
|
||
86 | trailing = 'trailing' in options ? !!options.trailing : trailing; |
||
87 | } |
||
88 | |||
89 | function invokeFunc(time) { |
||
90 | var args = lastArgs,
|
||
91 | thisArg = lastThis; |
||
92 | |||
93 | lastArgs = lastThis = undefined;
|
||
94 | lastInvokeTime = time; |
||
95 | result = func.apply(thisArg, args); |
||
96 | return result;
|
||
97 | } |
||
98 | |||
99 | function leadingEdge(time) { |
||
100 | // Reset any `maxWait` timer.
|
||
101 | lastInvokeTime = time; |
||
102 | // Start the timer for the trailing edge.
|
||
103 | timerId = setTimeout(timerExpired, wait); |
||
104 | // Invoke the leading edge.
|
||
105 | return leading ? invokeFunc(time) : result;
|
||
106 | } |
||
107 | |||
108 | function remainingWait(time) { |
||
109 | var timeSinceLastCall = time - lastCallTime,
|
||
110 | timeSinceLastInvoke = time - lastInvokeTime, |
||
111 | timeWaiting = wait - timeSinceLastCall; |
||
112 | |||
113 | return maxing
|
||
114 | ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) |
||
115 | : timeWaiting; |
||
116 | } |
||
117 | |||
118 | function shouldInvoke(time) { |
||
119 | var timeSinceLastCall = time - lastCallTime,
|
||
120 | timeSinceLastInvoke = time - lastInvokeTime; |
||
121 | |||
122 | // Either this is the first call, activity has stopped and we're at the
|
||
123 | // trailing edge, the system time has gone backwards and we're treating
|
||
124 | // it as the trailing edge, or we've hit the `maxWait` limit.
|
||
125 | return (lastCallTime === undefined || (timeSinceLastCall >= wait) || |
||
126 | (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
|
||
127 | } |
||
128 | |||
129 | function timerExpired() { |
||
130 | var time = now();
|
||
131 | if (shouldInvoke(time)) {
|
||
132 | return trailingEdge(time);
|
||
133 | } |
||
134 | // Restart the timer.
|
||
135 | timerId = setTimeout(timerExpired, remainingWait(time)); |
||
136 | } |
||
137 | |||
138 | function trailingEdge(time) { |
||
139 | timerId = undefined;
|
||
140 | |||
141 | // Only invoke if we have `lastArgs` which means `func` has been
|
||
142 | // debounced at least once.
|
||
143 | if (trailing && lastArgs) {
|
||
144 | return invokeFunc(time);
|
||
145 | } |
||
146 | lastArgs = lastThis = undefined;
|
||
147 | return result;
|
||
148 | } |
||
149 | |||
150 | function cancel() { |
||
151 | if (timerId !== undefined) { |
||
152 | clearTimeout(timerId); |
||
153 | } |
||
154 | lastInvokeTime = 0;
|
||
155 | lastArgs = lastCallTime = lastThis = timerId = undefined;
|
||
156 | } |
||
157 | |||
158 | function flush() { |
||
159 | return timerId === undefined ? result : trailingEdge(now()); |
||
160 | } |
||
161 | |||
162 | function debounced() { |
||
163 | var time = now(),
|
||
164 | isInvoking = shouldInvoke(time); |
||
165 | |||
166 | lastArgs = arguments;
|
||
167 | lastThis = this;
|
||
168 | lastCallTime = time; |
||
169 | |||
170 | if (isInvoking) {
|
||
171 | if (timerId === undefined) { |
||
172 | return leadingEdge(lastCallTime);
|
||
173 | } |
||
174 | if (maxing) {
|
||
175 | // Handle invocations in a tight loop.
|
||
176 | timerId = setTimeout(timerExpired, wait); |
||
177 | return invokeFunc(lastCallTime);
|
||
178 | } |
||
179 | } |
||
180 | if (timerId === undefined) { |
||
181 | timerId = setTimeout(timerExpired, wait); |
||
182 | } |
||
183 | return result;
|
||
184 | } |
||
185 | debounced.cancel = cancel; |
||
186 | debounced.flush = flush; |
||
187 | return debounced;
|
||
188 | } |
||
189 | |||
190 | module.exports = debounce; |