JS实现多任务并发请求问题

JS实现多任务并发请求问题

Ajax、Axios、Fetch的区别

Ajax

传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,以下面登录请求为例,如果多个请求之间有先后关系的话,就会出现回调地狱问题。

$.ajax({
  url: 'http://127.0.0.1:8888/user/login',
  method: 'post',
  data: {
    username: 'zhansan',
    password: '123456'
  },
  success(res) {
    if (res.code === 0) {
      // 登录成功 获取列表信息
      $.ajax({
        url: 'http://127.0.0.1:8888/user/list',
        method: 'get',
        success(res) {
            console.log(res);
            ...
            ...
        }
      })
    }
  }
})

Axios

axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。配合asyns,await相对于JQajax最大的有点就是解决了回调地狱问题

(async function () {
  let result = await axios.post('/user/login', {
    username: 'admin',
    password: '123456'
  });
  result = await axios.get('/user/list');
  console.log(result);
  ...
  ...
})();

Fetch

fetch是ES6出的基于promise设计的API,需要注意的是,fetch不是对ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。

(async function () {
  let result = await fetch('/user/login', {
    method: 'post',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: {
      username: 'admin',
      password: '123456'
    }
  }).then(res => {
    return res.json();
  });

  result = await fetch('/user/list').then(res => {
    return res.json();
  });
  console.log(result);
  ...
  ...
})();

串行与并行

串行:请求是异步的,需要等待上一个请求结果成功,才会执行下一个请求。

并行:同时发送多个请求,待所有请求都成功才会去执行下一步操作

HTTP请求可以同时进行,但是JS是单线程,代码是逐行解析的。

Promise.all并发限制和async-pool的应用

const delay = function delay(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(time);
    }, time);
  });
};

// tasks中的每一项都是一个请求任务
let tasks = [delay(1000), delay(100), delay(3000), delay(2300), delay(900), delay(1500)]

console.time('时间')
Promise.all(tasks).then(results => {
  console.log(results);
  console.timeEnd('时间')
});

Promise.all所有的请求队列同时并发进行,只有所有的请求全部成功才会一次性将结果返回,所以总用时一定是最久的那个(长板效应)

let data = [];
  asyncPool(2, tasks, (task, next) => {
    task.then(res => {
      data.push(res);
        next();
      });
  }, () => {
  console.log(data);
});
// 第一次执行2个,成功就依次往下排...所有都完成直接返回结果

asyncPool可以限制并发的数量

使用JS实现并发请求接口的解决方案

  • 要点:tasks是一个数组,每一项就是一个异步请求「基于Promise管理」
  • 思路:根据并发数量创建对应工作区,依次将每一个任务放在工作区执行,每个工作区也基于Promise来管理
function createRequest(tasks, pool) {
  pool = pool || 5;
  let results = [],
      together = new Array(pool).fill(null),
      index = 0;
  together = together.map(() => {
    return new Promise((resolve, reject) => {
      const run = function run() {
        if (index >= tasks.length) {
          resolve();
          return;
        };
        let old_index = index,
          task = tasks[index++];
        task().then(result => {
          results[old_index] = result;
          run();
        }).catch(reason => {
          reject(reason);
        });
      };
      run();
    });
  });
  return Promise.all(together).then(() => results);
} 

createRequestAll(tasks, 2).then(res => {
  // 都成功,整体结果才成功,按顺序存储结果
  console.log('成功', res)
}).catch(err => {
  // 只要有失败,则整体失败
  console.log('失败', err)
})

这样就实现了,多个任务并发请求时的限制