diff --git a/packages/wxa-core/src/utils/fetch.js b/packages/wxa-core/src/utils/fetch.js index ee2d77d4..bed8bd44 100644 --- a/packages/wxa-core/src/utils/fetch.js +++ b/packages/wxa-core/src/utils/fetch.js @@ -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 */ @@ -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); }); } } @@ -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; } @@ -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', @@ -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; } } diff --git a/packages/wxa-core/src/utils/helpers.js b/packages/wxa-core/src/utils/helpers.js index d8b38597..517855b3 100644 --- a/packages/wxa-core/src/utils/helpers.js +++ b/packages/wxa-core/src/utils/helpers.js @@ -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}; +} diff --git a/packages/wxa-core/test/__snapshots__/fetch.test.js.snap b/packages/wxa-core/test/__snapshots__/fetch.test.js.snap index 540ab5a0..f5868a4b 100644 --- a/packages/wxa-core/test/__snapshots__/fetch.test.js.snap +++ b/packages/wxa-core/test/__snapshots__/fetch.test.js.snap @@ -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, diff --git a/packages/wxa-core/test/fetch.test.js b/packages/wxa-core/test/fetch.test.js index e6ed9092..163addfb 100644 --- a/packages/wxa-core/test/fetch.test.js +++ b/packages/wxa-core/test/fetch.test.js @@ -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'})); @@ -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); @@ -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, @@ -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); @@ -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'}); + }); +});