@@ -19,40 +19,67 @@ class Throttler<X extends AnyVoidFunction> implements InputRatePolicy<X> {
1919 this . args = null ;
2020 }
2121
22+ // If no timer is currently running, immediately call the function and set the
23+ // timer; if a timer is running out, just queue up the args for the call when
24+ // the timer runs out. Later calls during the same timeout will overwrite
25+ // earlier ones.
2226 normalCall ( ...args : Parameters < X > ) : void {
27+ // This will be an empty array (not null) if called without arguments, and
28+ // `[null]` if called with `null`.
2329 this . args = args ;
30+
31+ // Only invoke immediately if there isn't a timer running.
2432 if ( this . timerId === null ) {
2533 this . $invoke ( ) ;
26- this . timerId = setTimeout ( ( ) => {
27- // IE8 doesn't reliably clear timeout, so this additional
28- // check is needed
29- if ( this . timerId === null ) return ;
30- this . $clearTimer ( ) ;
31- if ( args . length > 0 ) this . normalCall ( ...args ) ;
32- } , this . delayMs ) ;
3334 }
3435 }
36+
37+ // Reset the timer if active and call immediately
3538 immediateCall ( ...args : Parameters < X > ) : void {
3639 this . $clearTimer ( ) ;
3740 this . args = args ;
3841 this . $invoke ( ) ;
3942 }
43+
44+ // Is there a call waiting to send?
4045 isPending ( ) : boolean {
41- return this . timerId !== null ;
46+ return this . args !== null ;
4247 }
48+
4349 $clearTimer ( ) : void {
4450 if ( this . timerId !== null ) {
4551 clearTimeout ( this . timerId ) ;
4652 this . timerId = null ;
4753 }
4854 }
55+
56+ // Invoke the throttled function with the currently-stored args and start the
57+ // timer.
4958 $invoke ( ) : void {
50- if ( this . args && this . args . length > 0 ) {
51- this . func . apply ( this . target , this . args ) ;
52- } else {
53- this . func . apply ( this . target ) ;
59+ if ( this . args === null ) {
60+ // Shouldn't get here, because $invoke should only be called right after
61+ // setting this.args. But just in case.
62+ return ;
5463 }
64+
65+ this . func . apply ( this . target , this . args ) ;
66+
67+ // Clear the stored args. This is used to track if a call is pending.
5568 this . args = null ;
69+
70+ // Set this.timerId to a newly-created timer, which will invoke a call with
71+ // the most recently called args (if any) when it expires.
72+ this . timerId = setTimeout ( ( ) => {
73+ // IE8 doesn't reliably clear timeout, so this additional check is needed
74+ if ( this . timerId === null ) return ;
75+
76+ this . $clearTimer ( ) ;
77+ // Do we have a call queued up?
78+ if ( this . isPending ( ) ) {
79+ // If so, invoke the call with queued args and reset timer.
80+ this . $invoke ( ) ;
81+ }
82+ } , this . delayMs ) ;
5683 }
5784}
5885
0 commit comments