Skip to content

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

Open
caluhm wants to merge 2 commits intoTanStack:mainfrom
caluhm:feat-fix-async-queuer-pending-tick
Open

fix(async-queuer): keep pendingTick true during wait period#189
caluhm wants to merge 2 commits intoTanStack:mainfrom
caluhm:feat-fix-async-queuer-pending-tick

Conversation

@caluhm
Copy link
Copy Markdown

@caluhm caluhm commented Mar 31, 2026

🎯 Changes

Fixes #188

When addItem() is called on a running AsyncQueuer during the wait period between item processing, it bypasses the configured wait delay and triggers immediate processing.

Root cause: In #tick(), pendingTick is set to false synchronously at the end of the method, even when async work (execute + wait timer) has been scheduled. Since addItem() checks isRunning && !pendingTick to decide whether to trigger #tick(), it incorrectly starts immediate processing during the wait period.

Fix: Track whether async work was scheduled in the while loop with a scheduledAsyncWork flag. Only clear pendingTick when no async work is pending. When async work is scheduled, pendingTick stays true until the next #tick() call (after execute() completes and the wait timer fires), preventing addItem() from triggering a premature tick.

Test added: Verifies that items added via addItem() during the wait period are not processed until the wait completes.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes
    • Corrected queuer behavior so items added during an active wait window do not bypass the configured wait delay.
  • Tests
    • Added test coverage verifying wait-period behavior when items are enqueued during processing.
  • Documentation
    • Updated reference docs’ source-location pointers for queuer-related entries.
  • Chores
    • Added release metadata for a patch update.

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 (execute + wait timer) was still pending. This caused
addItem() to trigger immediate processing that bypassed the configured wait.

This fix tracks whether async work was scheduled in the while loop and only
clears pendingTick when no async work is pending.

Closes TanStack#188
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f7d825f-628d-42ee-ba86-9899979c22ea

📥 Commits

Reviewing files that changed from the base of the PR and between b9f7dca and d1a8fe1.

📒 Files selected for processing (2)
  • docs/reference/classes/AsyncQueuer.md
  • docs/reference/functions/asyncQueue.md
✅ Files skipped from review due to trivial changes (2)
  • docs/reference/functions/asyncQueue.md
  • docs/reference/classes/AsyncQueuer.md

📝 Walkthrough

Walkthrough

AsyncQueuer's tick logic now tracks whether async work was scheduled during a tick and only clears the pendingTick flag when no async operations were scheduled. This prevents items added during the configured wait period from bypassing the wait and being processed immediately. A test was added and changeset created.

Changes

Cohort / File(s) Summary
Core logic
packages/pacer/src/async-queuer.ts
#tick() now sets a local scheduledAsyncWork flag when it schedules async executions and only clears pendingTick if no async work was scheduled, ensuring pendingTick remains true while asynchronous work/wait timers are outstanding.
Tests
packages/pacer/tests/async-queuer.test.ts
Added a test that verifies items enqueued during the wait interval are processed only after the wait completes (uses fake timers and concurrency=1 scenario).
Release metadata
.changeset/fix-async-queuer-pending-tick.md
New changeset added to publish a patch describing the pendingTick behavior fix for @tanstack/pacer.
Docs (line refs only)
docs/reference/classes/AsyncQueuer.md, docs/reference/functions/asyncQueue.md
Adjusted "Defined in" source line references to reflect the updated source locations; no API or behavioral documentation text changed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble at timers, twitch my whiskers true,
I keep pendingTick held tight so waits see through.
No sneaky skips, no hurried race —
Each item waits its proper place. 🐇⏳

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: keeping pendingTick true during the wait period to prevent premature tick execution.
Description check ✅ Passed The PR description fully addresses the required sections with detailed explanation of the issue, root cause, and solution, plus completed checklist items and proper changeset documentation.
Linked Issues check ✅ Passed The PR fully addresses the requirements from issue #188 by implementing the proposed fix with a scheduledAsyncWork flag to track async work and only clearing pendingTick when appropriate.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing issue #188: the async-queuer implementation fix, test case, changeset, and documentation line number updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/pacer/src/async-queuer.ts (1)

448-465: ⚠️ Potential issue | 🔴 Critical

Add error recovery to async work scheduling in #tick

When await this.execute() throws (e.g., throwOnError: true), the async branch exits before scheduling the next #tick() or wait timeout. Since scheduledAsyncWork is already true, pendingTick is never cleared at line 464, leaving it stuck true and preventing addItem() from retriggering processing (line 542 check: isRunning && !pendingTick stays false). This can permanently freeze the queue when errors occur during start().

🛠️ Proposed fix
      scheduledAsyncWork = true
      ;(async () => {
-       await this.execute()
-
-       const wait = this.#getWait()
-       if (wait > 0) {
-         const timeoutId = setTimeout(() => this.#tick(), wait)
-         this.#timeoutIds.add(timeoutId)
-         return
-       }
-
-       this.#tick()
+       try {
+         await this.execute()
+       } finally {
+         const wait = this.#getWait()
+         if (wait > 0) {
+           const timeoutId = setTimeout(() => this.#tick(), wait)
+           this.#timeoutIds.add(timeoutId)
+           return
+         }
+
+         this.#tick()
+       }
      })()
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/pacer/src/async-queuer.ts` around lines 448 - 465, The async IIFE in
`#tick` can exit early when await this.execute() throws, leaving the local
scheduledAsyncWork flag true and pendingTick stuck; wrap the call to
this.execute() in try/catch (or use try/finally) so that errors do not skip the
scheduling logic: on both success and error compute wait via this.#getWait(),
schedule the next tick (setTimeout + this.#timeoutIds.add or call this.#tick()),
and ensure you set scheduledAsyncWork = false before the surrounding if
(!scheduledAsyncWork) { this.#setState({ pendingTick: false }) } runs; reference
the async IIFE, scheduledAsyncWork, this.execute(), this.#getWait(),
this.#timeoutIds, this.#tick(), and this.#setState() when making the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/pacer/src/async-queuer.ts`:
- Around line 448-465: The async IIFE in `#tick` can exit early when await
this.execute() throws, leaving the local scheduledAsyncWork flag true and
pendingTick stuck; wrap the call to this.execute() in try/catch (or use
try/finally) so that errors do not skip the scheduling logic: on both success
and error compute wait via this.#getWait(), schedule the next tick (setTimeout +
this.#timeoutIds.add or call this.#tick()), and ensure you set
scheduledAsyncWork = false before the surrounding if (!scheduledAsyncWork) {
this.#setState({ pendingTick: false }) } runs; reference the async IIFE,
scheduledAsyncWork, this.execute(), this.#getWait(), this.#timeoutIds,
this.#tick(), and this.#setState() when making the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c61369d7-3b97-4b0b-8b87-b8be3aa6476d

📥 Commits

Reviewing files that changed from the base of the PR and between e71ead1 and b9f7dca.

📒 Files selected for processing (3)
  • .changeset/fix-async-queuer-pending-tick.md
  • packages/pacer/src/async-queuer.ts
  • packages/pacer/tests/async-queuer.test.ts

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 31, 2026

View your CI Pipeline Execution ↗ for commit d1a8fe1

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 17s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-31 13:16:31 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 31, 2026

More templates

@tanstack/angular-pacer

npm i https://pkg.pr.new/@tanstack/angular-pacer@189

@tanstack/pacer

npm i https://pkg.pr.new/@tanstack/pacer@189

@tanstack/pacer-devtools

npm i https://pkg.pr.new/@tanstack/pacer-devtools@189

@tanstack/pacer-lite

npm i https://pkg.pr.new/@tanstack/pacer-lite@189

@tanstack/preact-pacer

npm i https://pkg.pr.new/@tanstack/preact-pacer@189

@tanstack/preact-pacer-devtools

npm i https://pkg.pr.new/@tanstack/preact-pacer-devtools@189

@tanstack/react-pacer

npm i https://pkg.pr.new/@tanstack/react-pacer@189

@tanstack/react-pacer-devtools

npm i https://pkg.pr.new/@tanstack/react-pacer-devtools@189

@tanstack/solid-pacer

npm i https://pkg.pr.new/@tanstack/solid-pacer@189

@tanstack/solid-pacer-devtools

npm i https://pkg.pr.new/@tanstack/solid-pacer-devtools@189

commit: d1a8fe1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: AsyncQueuer addItem() bypasses wait period when called during active processing

1 participant