Skip to content

Commit 96e602e

Browse files
crisbetoalxhub
authored andcommitted
fix(core): cancel in-progress request when same value is assigned (#59280)
Fixes that `resource` wasn't cancelling its in-progress request if the same value as the current one is assigned. Fixes #59272. PR Close #59280
1 parent 9ed9c40 commit 96e602e

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

packages/core/src/resource/resource.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,11 @@ class ResourceImpl<T, R> extends BaseWritableResource<T> implements ResourceRef<
221221
}
222222

223223
const current = untracked(this.value);
224-
if (this.equal ? this.equal(current, value) : current === value) {
224+
225+
if (
226+
untracked(this.status) === ResourceStatus.Local &&
227+
(this.equal ? this.equal(current, value) : current === value)
228+
) {
225229
return;
226230
}
227231

packages/core/test/resource/resource_spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,59 @@ describe('resource', () => {
622622
stream.set({error: 'fail'});
623623
expect(res.value()).toBe(undefined);
624624
});
625+
626+
it('should interrupt pending request if the same value is set', async () => {
627+
const counter = signal(0);
628+
const backend = new MockEchoBackend<{counter: number} | null>();
629+
const aborted: ({counter: number} | null)[] = [];
630+
const echoResource = resource<{counter: number} | null, {counter: number} | null>({
631+
request: () => ({counter: counter()}),
632+
loader: ({request, abortSignal}) => {
633+
abortSignal.addEventListener('abort', () => backend.abort(request));
634+
return backend.fetch(request).catch((reason) => {
635+
if (reason === 'aborted') {
636+
aborted.push(request);
637+
}
638+
throw new Error(reason);
639+
});
640+
},
641+
injector: TestBed.inject(Injector),
642+
});
643+
644+
// Start the initial load.
645+
TestBed.flushEffects();
646+
await Promise.resolve();
647+
expect(echoResource.status()).toBe(ResourceStatus.Loading);
648+
expect(echoResource.value()).toBe(undefined);
649+
expect(echoResource.error()).toBe(undefined);
650+
expect(aborted).toEqual([]);
651+
652+
// Interrupt by setting a value before the request has resolved.
653+
echoResource.set(null);
654+
TestBed.flushEffects();
655+
await backend.flush();
656+
expect(echoResource.status()).toBe(ResourceStatus.Local);
657+
expect(echoResource.value()).toBe(null);
658+
expect(echoResource.error()).toBe(undefined);
659+
expect(aborted).toEqual([{counter: 0}]);
660+
661+
// Reload the resource to trigger another request.
662+
echoResource.reload();
663+
TestBed.flushEffects();
664+
await Promise.resolve();
665+
expect(echoResource.status()).toBe(ResourceStatus.Reloading);
666+
expect(echoResource.value()).toBe(null);
667+
expect(echoResource.error()).toBe(undefined);
668+
expect(aborted).toEqual([{counter: 0}]);
669+
670+
// Interrupt the reload with the same value as before.
671+
echoResource.set(null);
672+
await backend.flush();
673+
expect(echoResource.status()).toBe(ResourceStatus.Local);
674+
expect(echoResource.value()).toBe(null);
675+
expect(echoResource.error()).toBe(undefined);
676+
expect(aborted).toEqual([{counter: 0}, {counter: 0}]);
677+
});
625678
});
626679

627680
function flushMicrotasks(): Promise<void> {

0 commit comments

Comments
 (0)