2.7 KB98 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8export type DebouncedFunction<Args extends Array<unknown>> = {
9 (...args: Args): void;
10 reset: () => void;
11 isPending: () => boolean;
12 dispose: () => void;
13};
14
15/**
16 * This is a rate limiting mechanism, used to invoke a function after a repeated
17 * action has completed. This creates and returns a debounced version of
18 * the function passed in that will postpone its execution until after `wait`
19 * milliseconds have elapsed since the last time it was invoked.
20 *
21 * For example, if you wanted to update a preview after the user stops typing
22 * you could do the following:
23 *
24 * elem.addEventListener('keyup', debounce(this.updatePreview, 250), false);
25 *
26 * The returned function has a reset method which can be called to cancel a
27 * pending invocation.
28 *
29 * var debouncedUpdatePreview = debounce(this.updatePreview, 250);
30 * elem.addEventListener('keyup', debouncedUpdatePreview, false);
31 *
32 * // later, to cancel pending calls
33 * debouncedUpdatePreview.reset();
34 *
35 * @param func - the function to debounce
36 * @param wait - how long to wait in milliseconds
37 * @param context - optional context to invoke the function in
38 * @param leading - cause debounce to trigger the function on
39 * the leading edge instead of the trailing edge of the wait interval
40 */
41export function debounce<Args extends Array<unknown>>(
42 func: (...args: Args) => unknown,
43 wait: number,
44 context: unknown = undefined,
45 leading = false,
46): DebouncedFunction<Args> {
47 let timeout: NodeJS.Timeout | undefined;
48 let shouldCallLeading = true;
49
50 function debouncer(...args: Args) {
51 let callback: () => void;
52
53 if (leading) {
54 callback = function () {
55 shouldCallLeading = true;
56 clearTimeout(timeout);
57 timeout = undefined;
58 };
59
60 if (!shouldCallLeading) {
61 clearTimeout(timeout);
62 timeout = setTimeout(callback, wait);
63 return;
64 }
65
66 shouldCallLeading = false;
67 func.apply(context, args);
68 } else {
69 debouncer.reset();
70 callback = function () {
71 clearTimeout(timeout);
72 timeout = undefined;
73 func.apply(context, args);
74 };
75 }
76
77 clearTimeout(timeout);
78 timeout = setTimeout(callback, wait);
79 }
80
81 debouncer.reset = function () {
82 clearTimeout(timeout);
83 timeout = undefined;
84 shouldCallLeading = true;
85 };
86
87 debouncer.dispose = function () {
88 clearTimeout(timeout);
89 timeout = undefined;
90 };
91
92 debouncer.isPending = function () {
93 return timeout != null;
94 };
95
96 return debouncer;
97}
98