axios.all与Promise.all并发请求

前言

在工作中当我们的项目来到一个新的页面需要发多个请求,而这些请求的数据又毫不相干时,我们可以采取并发请求的方式。目前并发请求主要有Promise.all和axios.all两种方式,下面做详细介绍。

Promise.all

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.all([p1,p2,p3]);

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

p 的状态由 p1、p2、p3 决定,分成两种情况

  • (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

下面是一个具体的例子

// 3个函数都是封装的axios,返回promise实例的函数
let reqArr = [getNames(), getTypes(), getAges()]

 Promise.all(reqArr).then((resArr) => {
       console.log('请求结果', resArr)
 })

// 如果3个请求都成功,返回结果是存放3个请求结果的数组

axios.all与Promise.all并发请求_第1张图片

axios.all

axios.all的用法基本和Promise.all一致,因为axios.all方法就是对Promise.all方法进行了一层包装,本质上是一模一样的,没有任何额外的逻辑,所以调用axios.all方法就是调用了Promise.all方法。

注意:axios.all是axios的静态方法,不是axios实例的方法!可通过在main.js中引入axios,并将其挂载在vue原型上,实现全局使用

let reqArr = [getNames(), getTypes(), getAges()]

axios.all(reqArr).then((resArr) => {
     console.log('请求结果', resArr)
 })
// 返回结果同上

axios.all与Promise.all有区别的地方在于then中对于返回结果的处理,axios除了上面这种用法之外,还可以在then中调用axios.spread函数,axios.spread函数接受一个回调函数,回调函数的参数就是与请求相同顺序和数量的返回结果

let reqArr = [getNames(), getTypes(), getAges()]

 axios.all(reqArr).then(axios.spread((first, second, third)=>{
            console.log('结果1', first)
            console.log('结果2', second)
            console.log('结果3', third)
}))

返回结果
axios.all与Promise.all并发请求_第2张图片
axios.all与Promise.all之间的关系
axios.all方法就是对Promise.all方法进行了一层包装,本质上是一模一样的,没有任何额外的逻辑

  1. axios.all的本质
axios.all = function(promises) {
  return Promise.all(promises);
};
  1. axios.spread的本质
    axios.all方法与Promise.all方法是一模一样的,唯一看起来不同的地方就是then方法,我们先来比较这两个then方法中的内容:
// axios.all的then
axios.spread((first, second) => {})

// Promise.all的then
([first, second]) => {}

我们可以看到,Promise.all的then方法里面是个函数,函数的参数是所有请求的响应组成的数组;而axios.all的then方法里面调用了axios.spread方法,axios.spread方法接收一个函数作为参数,该参数函数的参数也是所有请求的响应,既然上文说了axios.all方法与Promise.all方法是一模一样的,那么我们只需想办法再让两个then方法相同即可。也就是说我们创建一个axios.spread方法并且让axios.spread((first, second) => {})的返回值与([first, second]) => {}等价即可。

axios.spread的实现

axios.all = function(promises) {
  return Promise.all(promises);
};
axios.spread = function(callback) {
  return function wrap(arr) {
    return callback.apply(null, arr);
  };
};

并发请求时的错误处理
  1. 给每个promise添加catch

根据Promise.all的用法我们知道,当参数数组中的所有promise实例都是fulfilled状态时,Promise.all的返回实例才会是fulfilled,否则返回实例的状态就是rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

这也就意味着我们并发的几个请求当中,只要有一个请求出错,就无法返回正常的结果。但我们希望有错误出现时,也能正常返回其他请求成功的结果。我们可以通过为每个请求添加catch错误处理来实现

let reqArr = [axios({method: 'get', url: 'www.baidu.com'}), getTypes(), getAges()]

// 为每个请求添加错误处理
 let handledErrReqs = reqArr.map(item => item.catch(error => {
        // 错误处理代码
        console.log('请求出错', error)
        
        // 也可以返回你想要的错误提示结果,如果不返回数据,那么获得的值就是undefined
        // return {msg: '请求出错'}
 }))
axios.all(handledErrReqs).then(axios.spread((first, second, third)=>{
        console.log('结果1', first)
        console.log('结果2', second)
        console.log('结果3', third)
 }))

以上方法对axios.all与Promise.all都适用

  1. 使用Promise.allSettled代替Promise.all

如果我们想获得reqArr 中的所有数据,不管他是成功还是失败
它的用法和Promise.all一致,只是返回结果有所差异

Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:

  • {status:“fulfilled”, value:result} 对于成功的响应
  • {status:“rejected”, reason:error} 对于 error

例如对于上面的例子

let reqArr = [axios({method: 'get', url: 'www.baidu.com'}), getTypes(), getAges()]

Promise.allSettled(reqArr).then(results => {
	console.log(results )
})

上面的results将会是

[
  {status: 'rejected', reason: ...error object...},
  {status: 'fulfilled', value: ...response...},
  {status: 'fulfilled', value: ...response...}
]

所以,对于每个 promise,我们都得到了其状态(status)和 value/reason。

注意:这个方法是最近新增的内容,所以存在一些兼容性问题,可通过polyfill解决。
如果浏览器不支持 Promise.allSettled,很容易进行 polyfill:

if (!Promise.allSettled) {
  const rejectHandler = reason => ({ status: 'rejected', reason });

  const resolveHandler = value => ({ status: 'fulfilled', value });

  Promise.allSettled = function (promises) {
    const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
    return Promise.all(convertedPromises);
  };
}

在这段代码中,promises.map 获取输入值,并通过 p => Promise.resolve§ 将输入值转换为 promise(以防传递了 non-promise),然后向每一个 promise 都添加 .then 处理程序(handler)。

这个处理程序(handler)将成功的结果 value 转换为 {status:‘fulfilled’, value},将 error reason 转换为 {status:‘rejected’, reason}。这正是 Promise.allSettled 的格式。

然后我们就可以使用 Promise.allSettled 来获取 所有 给定的 promise 的结果,即使其中一些被 reject。

参考文章:
【异步技术】Axios并发请求
axios添加axios.all和axios.spread方法,与promise.all
并发请求时错误处理
JavaScript Promise 对象

你可能感兴趣的:(javascript,vue.js,html5,es6)