11/**
22 * Message throttler for controlling the speed of streaming updates.
33 * Non-React version that can be used in Zustand stores or other non-component code.
4+ *
5+ * Automatically speeds up after a configurable duration to drain buffers faster.
46 */
57
68const DEFAULT_THROTTLE_DELAY_MS = 25
9+ const DEFAULT_FAST_DELAY_MS = 0
10+ const DEFAULT_SPEEDUP_AFTER_MS = 5_000
711
812export interface MessageThrottlerOptions < T > {
913 delayInMs ?: number
14+ fastDelayInMs ?: number
15+ speedupAfterMs ?: number
1016 onMessage : ( message : T ) => void
1117}
1218
@@ -15,16 +21,42 @@ export class MessageThrottler<T> {
1521 private timer : ReturnType < typeof setTimeout > | null = null
1622 private isProcessing = false
1723 private delayInMs : number
24+ private fastDelayInMs : number
25+ private speedupAfterMs : number
26+ private startTime : number | null = null
1827 private onMessage : ( message : T ) => void
1928
2029 constructor ( {
2130 delayInMs = DEFAULT_THROTTLE_DELAY_MS ,
31+ fastDelayInMs = DEFAULT_FAST_DELAY_MS ,
32+ speedupAfterMs = DEFAULT_SPEEDUP_AFTER_MS ,
2233 onMessage,
2334 } : MessageThrottlerOptions < T > ) {
2435 this . delayInMs = delayInMs
36+ this . fastDelayInMs = fastDelayInMs
37+ this . speedupAfterMs = speedupAfterMs
2538 this . onMessage = onMessage
2639 }
2740
41+ /**
42+ * Start the speedup timer. Call this when the first meaningful content arrives.
43+ */
44+ startSpeedupTimer ( ) : void {
45+ if ( this . startTime === null ) {
46+ this . startTime = Date . now ( )
47+ }
48+ }
49+
50+ private getCurrentDelay ( ) : number {
51+ if ( this . startTime === null ) {
52+ return this . delayInMs
53+ }
54+ const elapsed = Date . now ( ) - this . startTime
55+ return elapsed >= this . speedupAfterMs
56+ ? this . fastDelayInMs
57+ : this . delayInMs
58+ }
59+
2860 private processNext = ( ) => {
2961 if ( this . queue . length === 0 ) {
3062 this . isProcessing = false
@@ -36,7 +68,7 @@ export class MessageThrottler<T> {
3668 this . onMessage ( nextMessage )
3769
3870 if ( this . queue . length > 0 ) {
39- this . timer = setTimeout ( this . processNext , this . delayInMs )
71+ this . timer = setTimeout ( this . processNext , this . getCurrentDelay ( ) )
4072 } else {
4173 this . isProcessing = false
4274 this . timer = null
@@ -47,7 +79,7 @@ export class MessageThrottler<T> {
4779 this . queue . push ( message )
4880 if ( ! this . isProcessing ) {
4981 this . isProcessing = true
50- this . timer = setTimeout ( this . processNext , this . delayInMs )
82+ this . timer = setTimeout ( this . processNext , this . getCurrentDelay ( ) )
5183 }
5284 }
5385
0 commit comments