Skip to content

Commit de47c35

Browse files
committed
fix: Initial value for throttle queue timeMs
Also renamed the property to avoid clashes with the throttleMs option.
1 parent 1f7afe7 commit de47c35

File tree

3 files changed

+66
-16
lines changed

3 files changed

+66
-16
lines changed

packages/nuqs/src/lib/queues/debounce.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class DebouncedPromiseQueue<ValueType, OutputType> {
5252
// --
5353

5454
type DebouncedUpdateQueue = DebouncedPromiseQueue<
55-
Omit<UpdateQueuePushArgs, 'throttleMs'>,
55+
Omit<UpdateQueuePushArgs, 'timeMs'>,
5656
URLSearchParams
5757
>
5858

@@ -65,13 +65,13 @@ export class DebounceController {
6565
}
6666

6767
push(
68-
update: Omit<UpdateQueuePushArgs, 'throttleMs'>,
68+
update: Omit<UpdateQueuePushArgs, 'timeMs'>,
6969
timeMs: number,
7070
adapter: UpdateQueueAdapterContext
7171
): Promise<URLSearchParams> {
7272
if (!this.queues.has(update.key)) {
7373
const queue = new DebouncedPromiseQueue<
74-
Omit<UpdateQueuePushArgs, 'throttleMs'>,
74+
Omit<UpdateQueuePushArgs, 'timeMs'>,
7575
URLSearchParams
7676
>(update => {
7777
this.throttleQueue.push(update)

packages/nuqs/src/lib/queues/throttle.test.ts

+52-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { describe, expect, it } from 'vitest'
2-
import { ThrottledQueue } from './throttle'
1+
import { describe, expect, it, vi } from 'vitest'
2+
import type { UpdateUrlFunction } from '../../adapters/lib/defs'
3+
import { defaultRateLimit } from './rate-limiting'
4+
import { ThrottledQueue, type UpdateQueueAdapterContext } from './throttle'
35

46
describe('throttle: ThrottleQueue value queueing', () => {
57
it('should enqueue key & values', () => {
@@ -62,4 +64,52 @@ describe('throttle: ThrottleQueue option combination logic', () => {
6264
queue.push({ key: 'c', query: null, options: { shallow: true } })
6365
expect(queue.options.shallow).toEqual(false)
6466
})
67+
it('should compose transitions', async () => {
68+
const mockStartTransition = (callback: () => void) => {
69+
callback()
70+
}
71+
const startTransitionA = vi.fn().mockImplementation(mockStartTransition)
72+
const startTransitionB = vi.fn().mockImplementation(mockStartTransition)
73+
const mockAdapter: UpdateQueueAdapterContext = {
74+
updateUrl: vi.fn<UpdateUrlFunction>(),
75+
getSearchParamsSnapshot() {
76+
return new URLSearchParams()
77+
}
78+
}
79+
const queue = new ThrottledQueue()
80+
queue.push({
81+
key: 'a',
82+
query: null,
83+
options: { startTransition: startTransitionA }
84+
})
85+
queue.push({
86+
key: 'b',
87+
query: null,
88+
options: { startTransition: startTransitionB }
89+
})
90+
await queue.flush(mockAdapter)
91+
expect(startTransitionA).toHaveBeenCalledOnce()
92+
expect(startTransitionB).toHaveBeenCalledOnce()
93+
expect(startTransitionA).toHaveBeenCalledBefore(startTransitionB)
94+
})
95+
it('keeps the maximum value for timeMs', () => {
96+
const queue = new ThrottledQueue()
97+
queue.push({ key: 'a', query: null, options: {}, timeMs: 100 })
98+
queue.push({ key: 'b', query: null, options: {}, timeMs: 200 })
99+
queue.push({ key: 'c', query: null, options: {}, timeMs: 300 })
100+
expect(queue.timeMs).toEqual(300)
101+
})
102+
it('clamps the minimum value for timeMs to the default rate limit', () => {
103+
expect(defaultRateLimit.timeMs).toBeGreaterThan(10) // precondition
104+
const queue = new ThrottledQueue()
105+
queue.push({ key: 'a', query: null, options: {}, timeMs: 10 })
106+
expect(queue.timeMs).toEqual(defaultRateLimit.timeMs)
107+
})
108+
it('supports passing Infinity to the timeMs option (but can be cleared)', () => {
109+
const queue = new ThrottledQueue()
110+
queue.push({ key: 'a', query: null, options: {}, timeMs: Infinity })
111+
expect(queue.timeMs).toBe(Infinity)
112+
queue.push({ key: 'b', query: null, options: {}, timeMs: 100 })
113+
expect(queue.timeMs).toBe(100)
114+
})
65115
})

packages/nuqs/src/lib/queues/throttle.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type UpdateQueuePushArgs = {
2020
key: string
2121
query: string | null
2222
options: AdapterOptions & Pick<Options, 'startTransition'>
23-
throttleMs?: number
23+
timeMs?: number
2424
}
2525

2626
function getSearchParamsSnapshotFromLocation() {
@@ -34,7 +34,7 @@ export class ThrottledQueue {
3434
scroll: false,
3535
shallow: true
3636
}
37-
throttleMs = 0
37+
timeMs = defaultRateLimit.timeMs
3838
transitions: TransitionSet = new Set()
3939
resolvers: Resolvers<URLSearchParams> | null = null
4040
lastFlushedAt = 0
@@ -43,7 +43,7 @@ export class ThrottledQueue {
4343
key,
4444
query,
4545
options,
46-
throttleMs = defaultRateLimit.timeMs
46+
timeMs = defaultRateLimit.timeMs
4747
}: UpdateQueuePushArgs) {
4848
debug('[nuqs queue] Enqueueing %s=%s %O', key, query, options)
4949
// Enqueue update
@@ -60,9 +60,9 @@ export class ThrottledQueue {
6060
if (options.startTransition) {
6161
this.transitions.add(options.startTransition)
6262
}
63-
this.throttleMs = Math.max(
64-
throttleMs,
65-
Number.isFinite(this.throttleMs) ? this.throttleMs : 0
63+
this.timeMs = Math.max(
64+
timeMs,
65+
Number.isFinite(this.timeMs) ? this.timeMs : 0
6666
)
6767
}
6868

@@ -75,7 +75,7 @@ export class ThrottledQueue {
7575
rateLimitFactor = 1,
7676
...adapter
7777
}: UpdateQueueAdapterContext): Promise<URLSearchParams> {
78-
if (!Number.isFinite(this.throttleMs)) {
78+
if (!Number.isFinite(this.timeMs)) {
7979
debug('[nuqs queue] Skipping flush due to throttleMs=Infinity')
8080
return Promise.resolve(getSearchParamsSnapshot())
8181
}
@@ -107,14 +107,14 @@ export class ThrottledQueue {
107107
const runOnNextTick = () => {
108108
const now = performance.now()
109109
const timeSinceLastFlush = now - this.lastFlushedAt
110-
const throttleMs = this.throttleMs
110+
const timeMs = this.timeMs
111111
const flushInMs =
112112
rateLimitFactor *
113-
Math.max(0, Math.min(throttleMs, throttleMs - timeSinceLastFlush))
113+
Math.max(0, Math.min(timeMs, timeMs - timeSinceLastFlush))
114114
debug(
115115
`[nuqs queue] Scheduling flush in %f ms. Throttled at %f ms (x%f)`,
116116
flushInMs,
117-
throttleMs,
117+
timeMs,
118118
rateLimitFactor
119119
)
120120
if (flushInMs === 0) {
@@ -135,7 +135,7 @@ export class ThrottledQueue {
135135
this.options.history = 'replace'
136136
this.options.scroll = false
137137
this.options.shallow = true
138-
this.throttleMs = defaultRateLimit.timeMs
138+
this.timeMs = defaultRateLimit.timeMs
139139
}
140140

141141
applyPendingUpdates(

0 commit comments

Comments
 (0)