Skip to content

Commit 12af673

Browse files
authored
fix: aborted requests should not clear its cache afterwards if previous request was cached (#923)
1 parent cfd66e2 commit 12af673

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

src/interceptors/response.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
1515
*
1616
* Also update the waiting list for this key by rejecting it.
1717
*/
18-
const rejectResponse = async (responseId: string, config: CacheRequestConfig) => {
18+
const rejectResponse = async (
19+
responseId: string,
20+
config: CacheRequestConfig,
21+
clearCache: boolean
22+
) => {
1923
// Updates the cache to empty to prevent infinite loading state
20-
await axios.storage.remove(responseId, config);
24+
if (clearCache) {
25+
await axios.storage.remove(responseId, config);
26+
}
2127

2228
// Rejects the deferred, if present
2329
const deferred = axios.waiting.get(responseId);
@@ -116,7 +122,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
116122
!cache.data &&
117123
!(await testCachePredicate(response, cacheConfig.cachePredicate))
118124
) {
119-
await rejectResponse(response.id, config);
125+
await rejectResponse(response.id, config, true);
120126

121127
if (__ACI_DEV__) {
122128
axios.debug({
@@ -154,7 +160,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
154160

155161
// Cache should not be used
156162
if (expirationTime === 'dont cache') {
157-
await rejectResponse(response.id, config);
163+
await rejectResponse(response.id, config, true);
158164

159165
if (__ACI_DEV__) {
160166
axios.debug({
@@ -276,7 +282,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
276282
}
277283

278284
// Rejects all other requests waiting for this response
279-
await rejectResponse(id, config);
285+
await rejectResponse(id, config, true);
280286

281287
throw error;
282288
}
@@ -297,7 +303,12 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
297303
}
298304

299305
// Rejects all other requests waiting for this response
300-
await rejectResponse(id, config);
306+
await rejectResponse(
307+
id,
308+
config,
309+
// Do not clear cache if this request is cached, but the request was cancelled before returning the cached response
310+
error.code !== 'ERR_CANCELED' || (error.code === 'ERR_CANCELED' && cache.state !== 'cached')
311+
);
301312

302313
throw error;
303314
}
@@ -381,7 +392,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
381392
}
382393

383394
// Rejects all other requests waiting for this response
384-
await rejectResponse(id, config);
395+
await rejectResponse(id, config, true);
385396

386397
throw error;
387398
};

test/interceptors/response.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,47 @@ describe('Response Interceptor', () => {
361361
assert.equal(storage.state, 'cached');
362362
assert.equal(storage.data?.data, true);
363363
});
364+
365+
// https://github.com/arthurfiorette/axios-cache-interceptor/issues/922
366+
it('Aborted requests should preserve non-stale valid cache entries', async () => {
367+
const instance = Axios.create({});
368+
const axios = setupCache(instance, {});
369+
370+
const id = '1';
371+
372+
const cache = {
373+
data: true,
374+
headers: {},
375+
status: 200,
376+
statusText: 'Ok'
377+
};
378+
379+
// Cache request
380+
axios.storage.set(id, {
381+
state: 'cached',
382+
ttl: 5000,
383+
createdAt: Date.now(),
384+
data: cache
385+
});
386+
387+
// First request cancelled immediately
388+
const controller = new AbortController();
389+
const cancelled = axios.get('http://unknown.url.lan:1234', { id, signal: controller.signal });
390+
controller.abort();
391+
try {
392+
await cancelled;
393+
assert.fail('should have thrown an error');
394+
} catch (error: any) {
395+
assert.equal(error.code, 'ERR_CANCELED');
396+
}
397+
398+
// Second request cancelled after a macrotask
399+
const controller2 = new AbortController();
400+
const promise = axios.get('http://unknown.url.lan:1234', { id, signal: controller2.signal });
401+
// Wait for eventual request to be sent
402+
await new Promise((res) => setTimeout(res));
403+
controller2.abort();
404+
const response = await promise;
405+
assert.ok(response.cached);
406+
});
364407
});

0 commit comments

Comments
 (0)