Skip to content

Commit

Permalink
feat(优化): 取消当前请求;取消全部请求
Browse files Browse the repository at this point in the history
  • Loading branch information
mengxinssfd committed Apr 20, 2022
1 parent ca16369 commit 23d01d1
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 63 deletions.
49 changes: 47 additions & 2 deletions __test__/AxiosWrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@ import { routers } from './mock-server';
import { StatusHandlers, AxiosWrapper, CustomConfig } from '../src';

jest.mock('axios');
const map = new Map<string, Function>();
const mockCreate = (/*config: AxiosRequestConfig*/) => {
// console.log(config);
return function ({ url, data, params, method }) {
return (routers[url] || routers['404'])(data || params, method);
return function ({ url, data, params, method, cancelToken }) {
return new Promise((res, rej) => {
map.set(cancelToken, rej);
(routers[url] || routers['404'])(data || params, method).then(res, rej);
});
};
};
(axios.CancelToken.source as any).mockImplementation(() => {
const token = Math.floor(Math.random() * 0xffffffffff).toString(16);
return {
token,
cancel(msg?: string) {
map.get(token)?.(msg);
},
};
});

(axios as any).create.mockImplementation(mockCreate);

Expand Down Expand Up @@ -154,4 +167,36 @@ describe('AxiosWrapper', () => {
data: { code: 200, data: { username: 'get', id: 1 }, msg: 'success' },
});
});
test('cancel all', async () => {
const req = new AxiosWrapper<CustomConfig<true>>();
const get = req.methodFactory('get');
const reqList = [get('/user'), get('/user'), get('/user')];
req.cancelAll('test');

const res = await Promise.allSettled(reqList);

expect(res).toEqual([
{ status: 'rejected', reason: 'test' },
{ status: 'rejected', reason: 'test' },
{ status: 'rejected', reason: 'test' },
]);
});
test('cancel current', async () => {
const req = new AxiosWrapper<CustomConfig<true>>();
const get = req.methodFactory('get');
const res1 = get('/user');
req.cancelCurrentRequest('cancel1');
const res2 = get('/user');
req.cancelCurrentRequest('cancel2');
const res3 = get('/user');
req.cancelCurrentRequest('cancel3');

const res = await Promise.allSettled([res1, res2, res3]);

expect(res).toEqual([
{ status: 'rejected', reason: 'cancel1' },
{ status: 'rejected', reason: 'cancel2' },
{ status: 'rejected', reason: 'cancel3' },
]);
});
});
27 changes: 2 additions & 25 deletions __test__/Other.test.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
import axios, { AxiosRequestConfig } from 'axios';
import { routers as r } from './mock-server';
import { routers as r, useMockAxios } from './mock-server';
const routers = {
...r,
'/create': () => {
return Promise.resolve({ status: 200, data: { code: 500, msg: 111 } });
},
};
jest.mock('axios');
const mockCreate = (config: AxiosRequestConfig) => {
type InterceptorCB = (config: AxiosRequestConfig) => AxiosRequestConfig | void;
const interceptors = {
request: [] as InterceptorCB[],
};
function AxiosIns({ url, data, params }) {
const cfg = interceptors.request.reduce((prev, cur) => {
return cur(config) || config;
}, config);
if (cfg) Object.assign(config, cfg);
return (routers[url] || routers['404'])(data || params);
}
AxiosIns.interceptors = {
request: {
use: (cb: InterceptorCB) => {
interceptors.request.push(cb);
},
},
};
return AxiosIns;
};
useMockAxios(routers);

(axios as any).create.mockImplementation(mockCreate);
import Other from './Other';

describe('Other', () => {
Expand Down
29 changes: 2 additions & 27 deletions __test__/Primary.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,5 @@
import axios, { AxiosRequestConfig } from 'axios';
import { routers } from './mock-server';

jest.mock('axios');
const mockCreate = (config: AxiosRequestConfig) => {
type InterceptorCB = (config: AxiosRequestConfig) => AxiosRequestConfig | void;
const interceptors = {
request: [] as InterceptorCB[],
};
function AxiosIns({ url, data, params }) {
const cfg = interceptors.request.reduce((prev, cur) => {
return cur(config) || config;
}, config);
if (cfg) Object.assign(config, cfg);
return (routers[url] || routers['404'])(data || params);
}
AxiosIns.interceptors = {
request: {
use: (cb: InterceptorCB) => {
interceptors.request.push(cb);
},
},
};
return AxiosIns;
};

(axios as any).create.mockImplementation(mockCreate);
import { useMockAxios, routers } from './mock-server';
useMockAxios(routers);
import Primary from './Primary';

describe('Primary', () => {
Expand Down
33 changes: 33 additions & 0 deletions __test__/mock-server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Qs from 'qs';
import axios, { AxiosRequestConfig } from 'axios';

function buildRes(res: { code: number; data?: any; msg: string }) {
return Promise.resolve({ data: res, status: 200 });
Expand Down Expand Up @@ -31,3 +32,35 @@ export const routers = {
return Promise.resolve({ status: 200, data: '1' });
},
};
jest.mock('axios');
export function useMockAxios(routers: any) {
const mockCreate = (config: AxiosRequestConfig) => {
type InterceptorCB = (config: AxiosRequestConfig) => AxiosRequestConfig | void;
const interceptors = {
request: [] as InterceptorCB[],
};
function AxiosIns({ url, data, params, method }) {
const cfg = interceptors.request.reduce((prev, cur) => {
return cur(config) || config;
}, config);
if (cfg) Object.assign(config, cfg);
return (routers[url] || routers['404'])(data || params, method);
}
AxiosIns.interceptors = {
request: {
use: (cb: InterceptorCB) => {
interceptors.request.push(cb);
},
},
};
return AxiosIns;
};

(axios as any).create.mockImplementation(mockCreate);
(axios.CancelToken.source as any).mockReturnValue({
token: '111',
cancel() {
return 0;
},
});
}
42 changes: 33 additions & 9 deletions src/AxiosWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import axios, {
AxiosRequestConfig,
AxiosResponse,
Method,
Canceler,
CancelToken,
} from 'axios';
import Qs from 'qs';
import { Cache } from './Cache';
Expand All @@ -22,6 +24,10 @@ export class AxiosWrapper<CC extends CustomConfig<boolean> = CustomConfig<boolea
// 子类不可访问缓存
private readonly cache: Cache<AxiosRequestConfig, AxiosPromise>;

private readonly cancelerMap = new Map<CancelToken, Canceler>();

cancelCurrentRequest!: Canceler;

constructor(config: AxiosRequestConfig = {}, private customConfig = {} as CC) {
// 1、保存基础配置
this.axiosIns = axios.create(config);
Expand Down Expand Up @@ -59,7 +65,16 @@ export class AxiosWrapper<CC extends CustomConfig<boolean> = CustomConfig<boolea
}
// 处理axiosRequestConfig
protected handleAxiosRequestConfig(url: string, config: AxiosRequestConfig): AxiosRequestConfig {
const finalConfig: AxiosRequestConfig = { ...config, url };
// 缓存取消函数
const { cancel, token } = axios.CancelToken.source();
this.cancelerMap.set(token, cancel);
this.cancelCurrentRequest = (msg) => {
cancel(msg);
// 请求成功后去除取消函数
this.cancelerMap.delete(token);
};

const finalConfig: AxiosRequestConfig = { ...config, url, cancelToken: token };
finalConfig.method = finalConfig.method || 'get';
return finalConfig;
}
Expand Down Expand Up @@ -99,16 +114,18 @@ export class AxiosWrapper<CC extends CustomConfig<boolean> = CustomConfig<boolea
}

// 请求,子类不可更改
private _request(axiosConfig: AxiosRequestConfig, customConfig: CC) {
if (customConfig.useCache) {
private async _request(axiosConfig: AxiosRequestConfig, customConfig: CC) {
// 使用缓存
const useCache = customConfig.useCache;
if (useCache) {
const c = this.cache.get(axiosConfig);
if (c) {
return c;
}
if (c) return c;
}

// 请求
const res = this.axiosIns(axiosConfig);

const useCache = customConfig.useCache;
// 缓存
if (useCache) {
this.cache.set(axiosConfig, res, typeof useCache === 'object' ? useCache : undefined);
}
Expand Down Expand Up @@ -138,11 +155,15 @@ export class AxiosWrapper<CC extends CustomConfig<boolean> = CustomConfig<boolea
try {
// 3、请求
const response: AxiosResponse = await this._request(axiosConfig, customConfig);
// 移除cancel
this.cancelCurrentRequest();
// 4、请求结果数据结构处理
const data = this.transformRes<T>(axiosConfig, customConfig, response);
// 5、状态码处理,并返回结果
return this.handleResponse<T>(response, data, customConfig);
} catch (e: any) {
// 移除cancel
this.cancelCurrentRequest();
// 错误处理
const response: AxiosResponse<ResType<any>> = e.response;
const data = this.transformRes<T>(axiosConfig, customConfig, response);
Expand All @@ -154,8 +175,11 @@ export class AxiosWrapper<CC extends CustomConfig<boolean> = CustomConfig<boolea
}
}

// todo 取消请求
// cancelAllPending() {}
// 取消请求
cancelAll(msg?: string) {
this.cancelerMap.forEach((canceler) => canceler(msg));
this.cancelerMap.clear();
}

// 简单工厂:生成get post delete等method
methodFactory(method: Method) {
Expand Down

0 comments on commit 23d01d1

Please sign in to comment.