前言
在工作中当我们的项目来到一个新的页面需要发多个请求,而这些请求的数据又毫不相干时,我们可以采取并发请求的方式。目前并发请求主要有Promise.all和axios.all两种方式,下面做详细介绍。
Promise.all
Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
1 | 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的回调函数。
下面是一个具体的例子
1 2 3 4 5 6 7 8 | // 3个函数都是封装的axios,返回promise实例的函数 let reqArr = [getNames(), getTypes(), getAges()] Promise.all(reqArr).then((resArr) => {<!-- --> console.log('请求结果', resArr) }) // 如果3个请求都成功,返回结果是存放3个请求结果的数组 |
axios.all
axios.all的用法基本和Promise.all一致,因为axios.all方法就是对Promise.all方法进行了一层包装,本质上是一模一样的,没有任何额外的逻辑,所以调用axios.all方法就是调用了Promise.all方法。
注意:axios.all是axios的静态方法,不是axios实例的方法!可通过在main.js中引入axios,并将其挂载在vue原型上,实现全局使用
1 2 3 4 5 6 | let reqArr = [getNames(), getTypes(), getAges()] axios.all(reqArr).then((resArr) => {<!-- --> console.log('请求结果', resArr) }) // 返回结果同上 |
axios.all与Promise.all有区别的地方在于then中对于返回结果的处理,axios除了上面这种用法之外,还可以在then中调用axios.spread函数,axios.spread函数接受一个回调函数,回调函数的参数就是与请求相同顺序和数量的返回结果
1 2 3 4 5 6 7 | 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之间的关系
axios.all方法就是对Promise.all方法进行了一层包装,本质上是一模一样的,没有任何额外的逻辑
- axios.all的本质
1 2 3 | axios.all = function(promises) {<!-- --> return Promise.all(promises); }; |
- axios.spread的本质
axios.all方法与Promise.all方法是一模一样的,唯一看起来不同的地方就是then方法,我们先来比较这两个then方法中的内容:
1 2 3 4 5 | // 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的实现
1 2 3 4 5 6 7 8 | axios.all = function(promises) {<!-- --> return Promise.all(promises); }; axios.spread = function(callback) {<!-- --> return function wrap(arr) {<!-- --> return callback.apply(null, arr); }; }; |
并发请求时的错误处理
根据Promise.all的用法我们知道,当参数数组中的所有promise实例都是fulfilled状态时,Promise.all的返回实例才会是fulfilled,否则返回实例的状态就是rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
这也就意味着我们并发的几个请求当中,只要有一个请求出错,就无法返回正常的结果。但我们希望有错误出现时,也能正常返回其他请求成功的结果。我们可以通过为每个请求添加catch错误处理来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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都适用
参考文章:
【异步技术】Axios并发请求
axios添加axios.all和axios.spread方法,与promise.all
并发请求时错误处理
JavaScript Promise 对象