基于 Promise 实现异步任务队列 2
实现一个请求队列,要求同时请求数不能超过最大限制,调用方式的伪代码如下所示:
- // 第二个参数可以是fetch、axios等
- // const request = createRequest(6, fetch);
- // for (let i = 1; i <= 10; i++) {
- // request({ url: `xxx` }).then(res => {
- // // do somethings
- // });
- // }
JavaScript
首先对以上伪代码进行分析:
- createRequest 的返回值是一个函数
- request 是一个 Promise 实例
- request 可以重复多次调用
代码实现
- /**
- * 请求数量控制
- * @param {number} maxRequestCount 同时最大请求数量
- * @param {(opts:any)=>Promise} asyncFunc 异步请求函数,可以是axios,fetch等
- * @returns
- */
- function createRequest(maxRequestCount, asyncFunc) {
- // 正在请求中的request数量
- let requestingCount = 0;
- // 请求的队列
- let queue = [];
- function run() {
- // 如果正在请求的数量小于允许的最大数量,并且有需要执行的新的请求
- while (requestingCount < maxRequestCount && queue.length > 0) {
- // 从队列中取出待执行的任务
- const [options, resolve, reject] = queue.shift();
- requestingCount++;
- // 执行函数
- asyncFunc(options)
- .then(resolve)
- .catch(reject)
- .finally(() => {
- // 执行完成之后,将正在进行的任务数量减1
- requestingCount--;
- // 递归调用,判断是否有新的任务需要执行
- run();
- });
- }
- }
- return function (options) {
- // 返回一个promise对象
- return new Promise((resolve, reject) => {
- // 将收集到的参数信息及回调函数等放入请求队列
- queue.push([options, resolve, reject]);
- // 执行请求
- run();
- });
- };
- }
JavaScript
测试
- // 模拟一个异步请求
- function AsyncRequest(options) {
- const { url, method } = options;
- // 实际开发的时候直接返回一个axios、fetch等
- // return axios(url, options);
- // 模拟axios、fetch等返回一个Promise对象
- return new Promise(resolve => {
- // 随机延迟2-5s
- const timeout = Math.floor(Math.random() * 3) + 2;
- console.log(`${url} begin request.`);
- // 模拟接口情况
- setTimeout(() => {
- resolve(`${url} ${method} finished,Take ${timeout} seconds.`);
- }, timeout * 1e3);
- });
- }
- // 记录队列开始执行时间
- console.time('all time');
- console.time('main');
- // 设置最大请求数量
- const request = createRequest(6, AsyncRequest);
- // 实际开发的时候直接传入axios、fetch等第三方js库函数即可
- // const request = createRequest(6, axios);
- // 这里模拟进入页面之后,需要默认加载的一些数据
- for (let i = 1; i <= 10; i++) {
- request({ url: `url${i}`, method: 'get', data: `urlData${i}` }).then(res =>
- console.log(res)
- );
- }
- // 模拟用户行为,比如10s后用户点击了查询操作,触发了接口请求
- setTimeout(() => {
- request({ url: 'url_timeout', method: 'post', data: 'urlData_timeout' }).then(
- res => {
- console.log(res);
- console.timeEnd('all time');
- }
- );
- }, 10 * 1e3);
- // console.timeEnd('main');
- // url1 begin request.
- // url2 begin request.
- // url3 begin request.
- // url4 begin request.
- // url5 begin request.
- // url6 begin request.
- // main: 2.934ms
- // url4 get finished,Take 2 seconds.
- // url7 begin request.
- // url3 get finished,Take 3 seconds.
- // url8 begin request.
- // url1 get finished,Take 4 seconds.
- // url9 begin request.
- // url2 get finished,Take 4 seconds.
- // url10 begin request.
- // url5 get finished,Take 4 seconds.
- // url6 get finished,Take 4 seconds.
- // url8 get finished,Take 2 seconds.
- // url7 get finished,Take 4 seconds.
- // url10 get finished,Take 2 seconds.
- // url9 get finished,Take 4 seconds.
- // url_timeout begin request.
- // url_timeout post finished,Take 4 seconds.
- // all time: 14.008s
JavaScript