Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/fix-async-queuer-pending-tick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@tanstack/pacer': patch
---

fix(async-queuer): keep pendingTick true during wait period

When `addItem()` is called on a running queue during the wait period, it checks `isRunning && !pendingTick` to decide whether to trigger `#tick()`. Previously, `pendingTick` was set to `false` synchronously at the end of `#tick()`, even when async work was still pending. This caused `addItem()` to trigger immediate processing that bypassed the configured `wait` delay.

This fix tracks whether async work was scheduled and only clears `pendingTick` when no async work is pending.
6 changes: 5 additions & 1 deletion packages/pacer/src/async-queuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ export class AsyncQueuer<TValue> {
this.#checkExpiredItems()

// Process items concurrently up to the concurrency limit
let scheduledAsyncWork = false
const activeItems = this.store.state.activeItems
while (
activeItems.length < this.#getConcurrency() &&
Expand All @@ -444,6 +445,7 @@ export class AsyncQueuer<TValue> {
this.#setState({
activeItems,
})
scheduledAsyncWork = true
;(async () => {
await this.execute()

Expand All @@ -458,7 +460,9 @@ export class AsyncQueuer<TValue> {
})()
}

this.#setState({ pendingTick: false })
if (!scheduledAsyncWork) {
this.#setState({ pendingTick: false })
}
}

/**
Expand Down
34 changes: 34 additions & 0 deletions packages/pacer/tests/async-queuer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,40 @@ describe('AsyncQueuer', () => {
expect(results).toHaveLength(3)
expect(results[2]).toBe('third')
})

it('should respect wait period when addItem is called during processing', async () => {
const results: Array<string> = []
const asyncQueuer = new AsyncQueuer<string>(
async (item) => {
results.push(item)
return item
},
{
wait: 100,
concurrency: 1,
started: false,
},
)

asyncQueuer.addItem('first')
asyncQueuer.start()

// 'first' processes immediately
await vi.advanceTimersByTimeAsync(0)
expect(results).toEqual(['first'])

// During the 100ms wait period, add a new item
await vi.advanceTimersByTimeAsync(50)
asyncQueuer.addItem('second')

// 'second' should NOT have processed yet — still in the wait period
await vi.advanceTimersByTimeAsync(0)
expect(results).toEqual(['first'])

// After the remaining wait time, 'second' should process
await vi.advanceTimersByTimeAsync(50)
expect(results).toEqual(['first', 'second'])
})
})

describe('error handling', () => {
Expand Down
Loading