Skip to content

Commit

Permalink
feat(core): enabled abort request with fetch methods
Browse files Browse the repository at this point in the history
while passing configs.$withCancel equal to true, `fetch` methods will return an object, with defer,
request and a cancel function
  • Loading branch information
Genuifx committed Mar 1, 2020
1 parent 57d66a5 commit f7665ab
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 29 deletions.
78 changes: 57 additions & 21 deletions packages/wxa-core/src/utils/fetch.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import wxapi from './wxapi';
import {getPromise} from './helpers.js';

let MAXREQUEST = 5; // 最大请求数
// 增加请求队列
let requestQueue = [];
// 请求排队队列
let waitQueue = [];
// 标记请求
let requestMap = new Map();
/**
* 请求的uuid
*/
Expand Down Expand Up @@ -97,24 +99,31 @@ function continueQueue() {
// 队列里面有任务,继续请求。
while (requestQueue.length < MAXREQUEST && waitQueue.length) {
// 出队
let {resolve, reject, configs} = waitQueue.shift();
// 获取uuid, 根据uuid唯一确定某个请求
let uuid = new Uuid(requestQueue).get();
let {resolve, reject, configs, uuid} = waitQueue.shift();
// 压入请求队列
requestQueue.push({resolve, reject, configs, uuid});
// 记录请求
cacheRequest.record(configs);

$$fetch(configs).then((succ)=>{
let promise = $$fetch(configs);
// 记录请求
requestMap.set(uuid, promise.$requestTask);

promise.then((succ)=>{
let idx = requestQueue.findIndex((req)=>req.uuid===uuid);
if (idx >-1) requestQueue.splice(idx, 1);
continueQueue();
resolve(succ);

requestMap.delete(uuid);
}, (fail)=>{
let idx = requestQueue.findIndex((req)=>req.uuid===uuid);
if (idx >-1) requestQueue.splice(idx, 1);
continueQueue();
reject(fail);

requestMap.delete(uuid);
});
}
}
Expand All @@ -138,16 +147,25 @@ function $$fetch(configs) {
...axiosConfigs,
};

return wxapi(wx).request(postconfig)
.then((response)=>{
if (response && response.statusCode === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
}, (fail)=>{
return Promise.reject(fail);
// 改写 promise
let defer = getPromise();
let requestTask = wx.request({
...postconfig,
success(response) {
if (response && response.statusCode === 200) {
defer.resolve(response);
} else {
defer.reject(response);
}
},
fail(fail) {
defer.reject(fail);
},
});

defer.promise.$requestTask = requestTask;

return defer.promise;
}


Expand All @@ -162,9 +180,10 @@ function $$fetch(configs) {
*/
export default function fetch(url, data = {}, axiosConfigs = {}, method = 'get') {
let configs = {url, data, axiosConfigs, method};
let {$top, $noCache} = axiosConfigs;
let {$top, $noCache, $withCancel} = axiosConfigs;
delete axiosConfigs.$top;
delete axiosConfigs.$noCache;
delete axiosConfigs.$withCancel;

axiosConfigs = {
dataType: 'json',
Expand All @@ -183,13 +202,30 @@ export default function fetch(url, data = {}, axiosConfigs = {}, method = 'get')
}

function $request() {
return new Promise((resolve, reject) => {
// 排队
$top ?
waitQueue.unshift({resolve, reject, configs}) :
waitQueue.push({resolve, reject, configs});
continueQueue();
});
let defer = getPromise();
// 排队
// 获取uuid, 根据uuid唯一确定某个请求
let uuid = new Uuid(requestQueue).get();

$top ?
waitQueue.unshift({resolve: defer.resolve, reject: defer.reject, configs, uuid}) :
waitQueue.push({resolve: defer.resolve, reject: defer.reject, configs, uuid});
continueQueue();

let cancel = () => {
let task = requestMap.get(uuid);

if (task) {
// 已发送
task.abort();
} else {
// 待发送,出队
let idx = requestQueue.findIndex((req)=>req.uuid===uuid);
if (idx >-1) requestQueue.splice(idx, 1);
}
};

return $withCancel ? {request: defer.promise, defer, cancel} : defer.promise;
}
}

Expand Down
13 changes: 12 additions & 1 deletion packages/wxa-core/src/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,15 @@ export function compareVersion(v1, v2) {
}

return 0;
}
}

export function getPromise() {
let res;
let rej;
let promise = new Promise((resolve, reject)=>{
res = resolve;
rej = reject;
});

return {resolve: res, reject: rej, promise, defer: promise};
}
15 changes: 14 additions & 1 deletion packages/wxa-core/test/__snapshots__/fetch.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`fetch api normal 1`] = `
exports[`enaled request abort task make request with abort function 1`] = `
Object {
"cancel": [Function],
"defer": Object {
"defer": Promise {},
"promise": Promise {},
"reject": [Function],
"resolve": [Function],
},
"request": Promise {},
}
`;

exports[`fetch api just make a normal request 1`] = `
Object {
"error": "User with 6 not found.",
"statusCode": 200,
Expand Down
49 changes: 43 additions & 6 deletions packages/wxa-core/test/fetch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,33 @@ import {
setRequestExpiredTime,
} from '../src/utils/fetch';

let originConsole = console;
const users = {
4: {name: 'Mark'},
5: {name: 'Paul'},
10: {name: 'Ives'},
};

beforeAll(()=>{
global.wx.request = function(options) {
const userID = parseInt(options.url.substr('/users/'.length), 10);
let res = () => userID < 0 ? options.success({statusCode: 404}) : users[userID]
? options.success({statusCode: 200, data: users[userID]})
: options.fail({
statusCode: 200,
error: 'User with ' + userID + ' not found.',
});
let id = setTimeout(res, 50);

return {abort: ()=> {
clearTimeout(id);
options.fail({errMsg: 'request:fail abort'});
}};
};
});

describe('fetch api', ()=>{
let wrapStatusCode = (data)=>({statusCode: 200, data});
test('normal', async ()=>{
test('just make a normal request', async () => {
await expect(fetch('/users/4', {}, {}, 'post')).resolves.toEqual(wrapStatusCode({name: 'Mark'}));

await expect(fetch('/users/5', {}, {}, 'post')).resolves.toEqual(wrapStatusCode({name: 'Paul'}));
Expand All @@ -19,11 +42,11 @@ describe('fetch api', ()=>{
await expect(fetch('/users/6', {}, {}, 'post')).rejects.toMatchSnapshot();
});

test('404', async ()=>{
test('404 page', async ()=>{
await expect(fetch('/users/-1', {}, {}, 'post')).rejects.toMatchObject({statusCode: 404});
});

test('multi request', async ()=>{
test('make multi request one time', async ()=>{
let c1 = jest.fn();
await fetch('/users/1', {}, {}, 'post').catch(c1);
await fetch('/users/2', {}, {}, 'post').catch(c1);
Expand All @@ -37,7 +60,7 @@ describe('fetch api', ()=>{
await expect(fetch('/users/10', {}, {}, 'post')).resolves.toEqual(wrapStatusCode({name: 'Ives'}));
});

test('reject same request', async ()=>{
test('reject same request in expired time (default 500ms)', async ()=>{
let warn = jest.fn();
global.console = {
warn,
Expand Down Expand Up @@ -68,7 +91,7 @@ describe('fetch api', ()=>{
expect(c1).toHaveBeenCalledTimes(3);
});

test('setRequestExpiredTime', async ()=>{
test('setup request expired time', async ()=>{
expect(setRequestExpiredTime(void(0))).toBe(null);
expect(setRequestExpiredTime(null)).toBe(null);

Expand All @@ -84,3 +107,17 @@ describe('fetch api', ()=>{
await expect(fetch('/users/1', {}, {}, 'post')).rejects.toEqual({data: {code: -101, msg: '重复的请求'}});
});
});

describe('enaled request abort task', ()=>{
test('make request with abort function', async ()=>{
let req = fetch('/users/4', {}, {$withCancel: true});

expect(req).toMatchSnapshot();
expect(req.request).not.toBeFalsy();
expect(req.defer).not.toBeFalsy();
expect(req.cancel).not.toBeFalsy();

req.cancel();
await expect(req.request).rejects.toMatchObject({errMsg: 'request:fail abort'});
});
});

0 comments on commit f7665ab

Please sign in to comment.