前言:上篇文章中,我们以及构建出了基本的then()方法,并且处理掉了一些关键性问题,如链式调用怎么二次封装,链式调用then()方法返回值如何,并且完整的列举出了三种情况,分别是:非Promise,Promise(成功Promise、失败Promise)、抛出异常 , 时的返回值情况。最后我们留下了一个问题,就是有关这次文章的主题,那就是all()方法中,一次传入多个Promise组成的数组,最终结果如何,我们又该如何完美的封装它?
首先,我们就需要了解官方的Promise.all()方法是怎么运作的,请看以下代码:
let p1 = new Promise((resolve, reject) => {
resolve("OK")
})
let p2 = new Promise((resolve, reject) => {
reject("success")
})
let p3 = new Promise((resolve, reject) => {
resolve("successful")
})
const arr = [p1, p2, p3]
let result = Promise.all(arr)
console.log(result)
结果:
那么我们稍作改动:
let p1 = new Promise((resolve, reject) => {
resolve("OK")
})
let p2 = new Promise((resolve, reject) => {
reject("Oh no!")
})
let p3 = new Promise((resolve, reject) => {
resolve("successful")
})
const arr = [p1, p2, p3]
let result = Promise.all(arr)
console.log(result)
结果:
再在代码块某处添加抛出异常:
let p1 = new Promise((resolve, reject) => {
resolve("OK")
})
let p2 = new Promise((resolve, reject) => {
throw("Error!")
reject("Oh no!")
})
let p3 = new Promise((resolve, reject) => {
resolve("successful")
})
const arr = [p1, p2, p3]
let result = Promise.all(arr)
console.log(result)
结果可想而知:
从上面三种情况我们就以及完全了解了all()方法功能执行的结果如何了,并且对不同情况有着不同的处理:
1. 传入的Promises都成功了,那么all返回的就是一个成功的Promise对象,并且该对象的结果就是所有传入的Promise的成功的值组成的数组
2. 传入的Promises其中某个或者某些失败了,那么all方法返回的则是一个失败的Promise对象,并且失败的值为传入的Promises的第一个失败的Promise的值
3. 抛出异常,all方法返回的是一个失败的Promise,并且失败的值为抛出异常的值
由此可见,我们就能够构建出一个all方法出来,注意,因为存在链式调用,所以封装all方法时肯定是和then方法差不多的,也需要二次封装,即内部需要调用then方法,那么以上三种情况呢,抛出异常的情况就不需要考虑了,因为then方法中已经封装好了,同样的另一方面也因为在Promise系列方法中抛出异常的处理手段都是一样的,之后也就不需要考虑抛出异常的封装了,那么我们就可以得到:
// all 方法封装
Promise.all = function (promises) {
// 返回结果为Promise对象
return new Promise((resolve, reject) => {
// 存放返回结果 【数组】
let arr = []
// 遍历promises
for (let i = 0; i < promises.length; i++) {
Promises[i].then(v => {
}, r => {
reject(r)
})
}
})
}
all方法的封装需要注意的地方就在于,成功的情况比较苛刻,需要传入的所有的Promise都成功,并且最终结果得是所有的Promise对象成功的值组成的数组,那么什么时候判断为成功呢?(因为成功之后才能改变返回的Promise状态)我们可以声明一个计数器,当计数器没有等于传入的promises长度时,表示传入的Promise还没有遍历完,但是已经遍历完的Promise是可以确定为成功的,不成功那就回走另一条路(失败,调用reject())了,所有这时候我们只需要先将遍历完的Promise的值存在arr(存放成功后返回的结果)当中即可,当且仅当计数器的值等于传入的数组长度时,说明所有的Promise都遍历完了且都成功了,这时候我们调用resolve()改变返回的Promise的状态就行了:
// all 方法封装
Promise.all = function (promises) {
// 返回结果为Promise对象
return new Promise((resolve, reject) => {
// 计数器
let count = 0
// 存放返回结果 【数组】
let arr = []
// 遍历promises
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
// 当计数器的值等于promise的长度时 , 表示传入的promises中
// 所有的promise对象都是成功的,此时才需要改变返回的promise
// 的状态为成功,否则,计数器加一(count++)
arr[i] = v
if (count === promises.length) {
resolve(arr)
}
}, r => {
reject(r)
})
}
})
}
值得一提的事,这里为什么用到的事arr[i] = v ,而不是push方法呢,其实本质上来说都没啥区别,都是正确的,这里只是为了让结果更贴切官方库,官方库的返回结果是和传入的Promise成一一对应的,如果用push或者其他的存在异步调用的情况就会出现不一致的情况。
接下来会有相关文章持续更新之后的Promise的优化和相关的 then() 、all() 等函数的封装