From b053c449c0c62766394a74ac91424754b94d7dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9D=A4MrPork=E2=9D=A4?= <5321067+MrPorky@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:09:15 +0200 Subject: [PATCH 1/2] fix: correct maybeExecuteCount increment and add test for AbortSignal cancellation --- .changeset/humble-coats-shop.md | 5 +++++ packages/pacer/src/async-debouncer.ts | 2 +- packages/pacer/tests/async-debouncer.test.ts | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .changeset/humble-coats-shop.md diff --git a/.changeset/humble-coats-shop.md b/.changeset/humble-coats-shop.md new file mode 100644 index 000000000..d41bcaf07 --- /dev/null +++ b/.changeset/humble-coats-shop.md @@ -0,0 +1,5 @@ +--- +'@tanstack/pacer': patch +--- + +Fixed a bug in AsyncDebouncer where getAbortSignal() incorrectly returned null due to an internal execution ID mismatch. Abort signals can now be properly attached to underlying async tasks. diff --git a/packages/pacer/src/async-debouncer.ts b/packages/pacer/src/async-debouncer.ts index d24d721a7..faf13db0b 100644 --- a/packages/pacer/src/async-debouncer.ts +++ b/packages/pacer/src/async-debouncer.ts @@ -362,7 +362,7 @@ export class AsyncDebouncer { ...args: Parameters ): Promise | undefined> => { if (!this.#getEnabled()) return undefined - const currentMaybeExecuteCount = this.store.state.maybeExecuteCount + 1 + const currentMaybeExecuteCount = this.store.state.maybeExecuteCount try { this.#setState({ isExecuting: true }) diff --git a/packages/pacer/tests/async-debouncer.test.ts b/packages/pacer/tests/async-debouncer.test.ts index 66ac0ca99..06fe1f71f 100644 --- a/packages/pacer/tests/async-debouncer.test.ts +++ b/packages/pacer/tests/async-debouncer.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi, } from 'vitest' import { AsyncDebouncer, asyncDebounce } from '../src/async-debouncer' describe('AsyncDebouncer', () => { @@ -1358,5 +1358,22 @@ describe('asyncDebounce helper function', () => { expect(typeof debouncer.getAbortSignal).toBe('function') expect(debouncer.getAbortSignal()).toBeNull() }) + + it('should return an AbortSignal that can be used to cancel execution', async () => { + const debouncer = new AsyncDebouncer( + async () => { + const signal = debouncer.getAbortSignal() + expect(signal).toBeInstanceOf(AbortSignal) + return 'result' + }, + { wait: 300 }, + ) + + debouncer.maybeExecute() + vi.advanceTimersByTime(150) + const promise = debouncer.maybeExecute() + vi.advanceTimersByTime(300) + await promise + }) }) }) From 3e3b7e152f5412492be4fa91899b979ba61f537a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9D=A4MrPork=E2=9D=A4?= <5321067+MrPorky@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:41:35 +0200 Subject: [PATCH 2/2] fix: update AbortSignal test to verify aborted state on cancel --- packages/pacer/tests/async-debouncer.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/pacer/tests/async-debouncer.test.ts b/packages/pacer/tests/async-debouncer.test.ts index 06fe1f71f..a5f6a31c9 100644 --- a/packages/pacer/tests/async-debouncer.test.ts +++ b/packages/pacer/tests/async-debouncer.test.ts @@ -1359,20 +1359,25 @@ describe('asyncDebounce helper function', () => { expect(debouncer.getAbortSignal()).toBeNull() }) - it('should return an AbortSignal that can be used to cancel execution', async () => { + + it('should return an AbortSignal and mark it aborted when abort() is called', async () => { const debouncer = new AsyncDebouncer( async () => { const signal = debouncer.getAbortSignal() expect(signal).toBeInstanceOf(AbortSignal) + expect(signal?.aborted).toBe(false) return 'result' }, { wait: 300 }, ) + debouncer.maybeExecute() vi.advanceTimersByTime(150) const promise = debouncer.maybeExecute() vi.advanceTimersByTime(300) + debouncer.abort() + expect(debouncer.getAbortSignal()?.aborted ?? true).toBe(true) await promise }) })