Skip to content

Commit a2982d5

Browse files
committed
Fix PendingTasks for offline mode
1 parent f8ae27e commit a2982d5

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,54 @@ describe('PendingTasks Integration', () => {
230230
expect(query.data()).toMatch(/^data-\d+$/)
231231
})
232232

233+
test('should keep PendingTasks active when query starts offline (never reaches fetching)', async () => {
234+
const app = TestBed.inject(ApplicationRef)
235+
236+
onlineManager.setOnline(false)
237+
238+
const query = TestBed.runInInjectionContext(() =>
239+
injectQuery(() => ({
240+
queryKey: ['start-offline'],
241+
networkMode: 'online', // Default: won't fetch while offline
242+
queryFn: async () => {
243+
await sleep(10)
244+
return 'online-data'
245+
},
246+
})),
247+
)
248+
249+
// Allow query to initialize
250+
await Promise.resolve()
251+
await vi.advanceTimersByTimeAsync(0)
252+
253+
// Query should initialize directly to 'paused' (never goes through 'fetching')
254+
expect(query.status()).toBe('pending')
255+
expect(query.fetchStatus()).toBe('paused')
256+
257+
const stablePromise = app.whenStable()
258+
let stableResolved = false
259+
void stablePromise.then(() => {
260+
stableResolved = true
261+
})
262+
263+
await Promise.resolve()
264+
265+
// PendingTasks should block stability even though we never hit 'fetching'
266+
expect(stableResolved).toBe(false)
267+
268+
// Bring the app back online so the query can fetch
269+
onlineManager.setOnline(true)
270+
271+
await vi.advanceTimersByTimeAsync(20)
272+
await Promise.resolve()
273+
274+
await stablePromise
275+
276+
expect(stableResolved).toBe(true)
277+
expect(query.status()).toBe('success')
278+
expect(query.data()).toBe('online-data')
279+
})
280+
233281
test('should keep PendingTasks active while query retry is paused offline', async () => {
234282
const app = TestBed.inject(ApplicationRef)
235283
let attempt = 0

packages/angular-query-experimental/src/create-base-query.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ export function createBaseQuery<
4545
TQueryKey
4646
> | null = null
4747

48+
let taskCleanupRef: (() => void) | null = null
49+
50+
const startPendingTask = () => {
51+
if (!taskCleanupRef) {
52+
taskCleanupRef = pendingTasks.add()
53+
}
54+
}
55+
56+
const stopPendingTask = () => {
57+
if (taskCleanupRef) {
58+
taskCleanupRef()
59+
taskCleanupRef = null
60+
}
61+
}
62+
4863
/**
4964
* Signal that has the default options from query client applied
5065
* computed() is used so signals can be inserted into the options
@@ -111,18 +126,14 @@ export function createBaseQuery<
111126
}
112127

113128
observer = new Observer(queryClient, options)
114-
let taskCleanupRef: (() => void) | null = null
115129

116130
const unsubscribe = observer.subscribe(
117131
notifyManager.batchCalls((state) => {
118132
ngZone.run(() => {
119-
if (state.fetchStatus === 'fetching' && !taskCleanupRef) {
120-
taskCleanupRef = pendingTasks.add()
121-
}
122-
123-
if (state.fetchStatus === 'idle' && taskCleanupRef) {
124-
taskCleanupRef()
125-
taskCleanupRef = null
133+
if (state.fetchStatus !== 'idle') {
134+
startPendingTask()
135+
} else {
136+
stopPendingTask()
126137
}
127138

128139
if (
@@ -146,7 +157,7 @@ export function createBaseQuery<
146157
)
147158
destroyRef.onDestroy(() => {
148159
unsubscribe()
149-
taskCleanupRef?.()
160+
stopPendingTask()
150161
})
151162
}
152163

0 commit comments

Comments
 (0)