@@ -85,38 +85,28 @@ export function memoize<T, U extends any[]>(fn: (...args: U) => T): (...args: U)
8585 * Multiple calls made during the debounce window will receive references to the
8686 * same Promise similar to {@link shared}. The window will also be 'rolled', delaying
8787 * the execution by another {@link delay} milliseconds.
88+ *
89+ * This function prevents execution until {@link delay} milliseconds have passed
90+ * since the last invocation regardless of arguments. If this should be
91+ * argument dependent, look into {@link keyedDebounce}
8892 */
89- export function debounce < T > ( cb : ( ) => T | Promise < T > , delay : number = 0 ) : ( ) => Promise < T > {
90- let timeout : Timeout | undefined
91- let promise : Promise < T > | undefined
92-
93- return ( ) => {
94- timeout ?. refresh ( )
95-
96- return ( promise ??= new Promise < T > ( ( resolve , reject ) => {
97- timeout = new Timeout ( delay )
98- timeout . onCompletion ( async ( ) => {
99- timeout = promise = undefined
100- try {
101- resolve ( await cb ( ) )
102- } catch ( err ) {
103- reject ( err )
104- }
105- } )
106- } ) )
107- }
93+ export function debounce < Input extends any [ ] , Output > (
94+ cb : ( ...args : Input ) => Output | Promise < Output > ,
95+ delay : number = 0
96+ ) : ( ...args : Input ) => Promise < Output > {
97+ return cancellableDebounce ( cb , delay ) . promise
10898}
10999
110100/**
111101 *
112- * Similar to {@link debounce}, but allows the function to be cancelled and allow callbacks to pass function parameters .
102+ * Similar to {@link debounce}, but allows the function to be cancelled.
113103 */
114- export function cancellableDebounce < T , U extends any [ ] > (
115- cb : ( ...args : U ) => T | Promise < T > ,
104+ export function cancellableDebounce < Input extends any [ ] , Output > (
105+ cb : ( ...args : Input ) => Output | Promise < Output > ,
116106 delay : number = 0
117- ) : { promise : ( ...args : U ) => Promise < T > ; cancel : ( ) => void } {
107+ ) : { promise : ( ...args : Input ) => Promise < Output > ; cancel : ( ) => void } {
118108 let timeout : Timeout | undefined
119- let promise : Promise < T > | undefined
109+ let promise : Promise < Output > | undefined
120110
121111 const cancel = ( ) : void => {
122112 if ( timeout ) {
@@ -127,15 +117,15 @@ export function cancellableDebounce<T, U extends any[]>(
127117 }
128118
129119 return {
130- promise : ( ...arg ) => {
120+ promise : ( ...args : Input ) => {
131121 timeout ?. refresh ( )
132122
133- return ( promise ??= new Promise < T > ( ( resolve , reject ) => {
123+ return ( promise ??= new Promise < Output > ( ( resolve , reject ) => {
134124 timeout = new Timeout ( delay )
135125 timeout . onCompletion ( async ( ) => {
136126 timeout = promise = undefined
137127 try {
138- resolve ( await cb ( ...arg ) )
128+ resolve ( await cb ( ...args ) )
139129 } catch ( err ) {
140130 reject ( err )
141131 }
@@ -145,3 +135,24 @@ export function cancellableDebounce<T, U extends any[]>(
145135 cancel : cancel ,
146136 }
147137}
138+
139+ /**
140+ *
141+ * Similar to {@link debounce}, but uses a key to determine if the function should be called yet rather than a timeout connected to the function itself.
142+ */
143+ export function keyedDebounce < T , U extends any [ ] , K extends string = string > (
144+ fn : ( key : K , ...args : U ) => Promise < T >
145+ ) : typeof fn {
146+ const pending = new Map < K , Promise < T > > ( )
147+
148+ return ( key , ...args ) => {
149+ if ( pending . has ( key ) ) {
150+ return pending . get ( key ) !
151+ }
152+
153+ const promise = fn ( key , ...args ) . finally ( ( ) => pending . delete ( key ) )
154+ pending . set ( key , promise )
155+
156+ return promise
157+ }
158+ }
0 commit comments