14 changed files with 77 additions and 314 deletions
@ -1,127 +0,0 @@ |
|||||
import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'; |
|
||||
|
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'; |
|
||||
|
|
||||
import { AxiosCanceler } from './canceler'; |
|
||||
|
|
||||
describe('axiosCanceler', () => { |
|
||||
let axiosCanceler: AxiosCanceler; |
|
||||
|
|
||||
beforeEach(() => { |
|
||||
axiosCanceler = new AxiosCanceler(); |
|
||||
}); |
|
||||
|
|
||||
it('should generate a unique request key', () => { |
|
||||
const config: AxiosRequestConfig = { |
|
||||
data: { name: 'test' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test', |
|
||||
}; |
|
||||
const requestKey = axiosCanceler.getRequestKey(config); |
|
||||
expect(requestKey).toBe('get:/test:{"id":1}:{"name":"test"}'); |
|
||||
}); |
|
||||
|
|
||||
it('should add a request and create an AbortController', () => { |
|
||||
const config: InternalAxiosRequestConfig = { |
|
||||
data: { name: 'test' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
const updatedConfig = axiosCanceler.addRequest(config); |
|
||||
expect(updatedConfig.signal).toBeInstanceOf(AbortSignal); |
|
||||
}); |
|
||||
|
|
||||
it('should cancel an existing request if a duplicate is added', () => { |
|
||||
const config: InternalAxiosRequestConfig = { |
|
||||
data: { name: 'test' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
axiosCanceler.addRequest(config); |
|
||||
const controller = axiosCanceler.pending.get( |
|
||||
'get:/test:{"id":1}:{"name":"test"}', |
|
||||
); |
|
||||
expect(controller).toBeDefined(); |
|
||||
|
|
||||
if (controller) { |
|
||||
const spy = vi.spyOn(controller, 'abort'); |
|
||||
|
|
||||
axiosCanceler.addRequest(config); |
|
||||
expect(spy).toHaveBeenCalled(); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
it('should remove a request', () => { |
|
||||
const config: AxiosRequestConfig = { |
|
||||
data: { name: 'test' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test', |
|
||||
}; |
|
||||
|
|
||||
axiosCanceler.addRequest(config as InternalAxiosRequestConfig); |
|
||||
axiosCanceler.removeRequest(config); |
|
||||
expect(axiosCanceler.pending.size).toBe(0); |
|
||||
}); |
|
||||
|
|
||||
it('should remove all pending requests', () => { |
|
||||
const config1: InternalAxiosRequestConfig = { |
|
||||
data: { name: 'test1' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test1', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
const config2: InternalAxiosRequestConfig = { |
|
||||
data: { name: 'test2' }, |
|
||||
method: 'get', |
|
||||
params: { id: 2 }, |
|
||||
url: '/test2', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
axiosCanceler.addRequest(config1); |
|
||||
axiosCanceler.addRequest(config2); |
|
||||
|
|
||||
axiosCanceler.removeAllPending(); |
|
||||
expect(axiosCanceler.pending.size).toBe(0); |
|
||||
}); |
|
||||
|
|
||||
it('should handle empty config gracefully', () => { |
|
||||
const config = {} as InternalAxiosRequestConfig; |
|
||||
const updatedConfig = axiosCanceler.addRequest(config); |
|
||||
expect(updatedConfig.signal).toBeInstanceOf(AbortSignal); |
|
||||
}); |
|
||||
|
|
||||
it('should handle undefined params and data gracefully', () => { |
|
||||
const config: InternalAxiosRequestConfig = { |
|
||||
method: 'get', |
|
||||
url: '/test', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
const requestKey = axiosCanceler.getRequestKey(config); |
|
||||
expect(requestKey).toBe('get:/test:{}:{}'); |
|
||||
}); |
|
||||
|
|
||||
it('should not abort if no controller exists for the request key', () => { |
|
||||
const config: InternalAxiosRequestConfig = { |
|
||||
data: { name: 'test' }, |
|
||||
method: 'get', |
|
||||
params: { id: 1 }, |
|
||||
url: '/test', |
|
||||
} as InternalAxiosRequestConfig; |
|
||||
|
|
||||
const requestKey = axiosCanceler.getRequestKey(config); |
|
||||
const spy = vi.spyOn(AbortController.prototype, 'abort'); |
|
||||
|
|
||||
axiosCanceler.addRequest(config); |
|
||||
axiosCanceler.pending.delete(requestKey); |
|
||||
axiosCanceler.addRequest(config); |
|
||||
|
|
||||
expect(spy).not.toHaveBeenCalled(); |
|
||||
}); |
|
||||
}); |
|
||||
@ -1,52 +0,0 @@ |
|||||
import type { |
|
||||
AxiosRequestConfig, |
|
||||
AxiosResponse, |
|
||||
InternalAxiosRequestConfig, |
|
||||
} from 'axios'; |
|
||||
|
|
||||
class AxiosCanceler { |
|
||||
public pending: Map<string, AbortController> = new Map(); |
|
||||
|
|
||||
// 添加请求
|
|
||||
public addRequest( |
|
||||
config: InternalAxiosRequestConfig, |
|
||||
): InternalAxiosRequestConfig { |
|
||||
const requestKey = this.getRequestKey(config); |
|
||||
if (this.pending.has(requestKey)) { |
|
||||
// 如果存在相同的请求,取消前一个请求
|
|
||||
const controller = this.pending.get(requestKey); |
|
||||
controller?.abort(); |
|
||||
} |
|
||||
|
|
||||
// 创建新的AbortController并添加到pending中
|
|
||||
const controller = new AbortController(); |
|
||||
config.signal = controller.signal; |
|
||||
this.pending.set(requestKey, controller); |
|
||||
|
|
||||
return config; |
|
||||
} |
|
||||
|
|
||||
// 生成请求的唯一标识
|
|
||||
public getRequestKey(config: AxiosRequestConfig): string { |
|
||||
const { data = {}, method, params = {}, url } = config; |
|
||||
return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 清除所有等待中的请求 |
|
||||
*/ |
|
||||
public removeAllPending(): void { |
|
||||
for (const [, abortController] of this.pending) { |
|
||||
abortController?.abort(); |
|
||||
} |
|
||||
this.pending.clear(); |
|
||||
} |
|
||||
|
|
||||
// 移除请求
|
|
||||
public removeRequest(config: AxiosRequestConfig | AxiosResponse): void { |
|
||||
const requestKey = this.getRequestKey(config); |
|
||||
this.pending.delete(requestKey); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export { AxiosCanceler }; |
|
||||
Loading…
Reference in new issue