在 [ 彻底掌握 Promise - 原生 Promise 的实现(三) ] 中我们已经实现了简易版本的 Promise ,下面这篇文章,主要讲述 Promise.all 方法的实现。
在 Promise 中内置了一些静态方法可以供我们更好的去处理异步操作,比如我们本篇文章要讲述的 Promise.all 静态方法的实现。
Promise.all
语法
Promise 接收一个可迭代对象作为参数,也就是 Array、Set的数据结构,并且只返回一个Promise 的实例,返回结果是一个数组,数组包含了所有的回调结果。
说明
此方法在集合多个 promise
的返回结果时很有用。
完成(Fulfillment):
如果传入的可迭代对象为空,Promise.all
会同步地返回一个已完成(resolved)状态的promise
。如果所有传入的 promise
都变为完成状态,或者传入的可迭代对象内没有 promise
,Promise.all
返回的 promise
异步地变为完成。在任何情况下,Promise.all
返回的 promise
的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise
值)。
失败/拒绝(Rejection):
如果传入的 promise
中有一个失败(rejected),Promise.all
异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise
是否完成。
示例
const promise1 = 3
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
返回结果
通过返回值我们可以发现,返回值是一个数组,数组内包含了我们传入数据的所有结果。
原生实现
通过逆解析的思路去考虑原生实现的话,有下面几个步骤
- 返回值可以调用 then 方法,所以我们要返回一个 Promise 对象
- 在所有值都执行完,或者有一个为失败,就立即返回相应状态
class iPromise {
// 通过 static 的关键字可以创建类的静态方法
static all(arr) {
// 创建一个保存所有结果的list
let resultList = []
const len = arr.length
// 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
return new iPromise((resolve, reject) => {
// 保存结果
function addData(key, value) {
resultList[key] = value
}
// 循环执行传入的回调函数获取返回结果
for (let i = 0; i < len; i++) {
// 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
if (arr[i] instanceof iPromise) {
// 执行这个 Promise 对象并获取返回结果
arr[i].then(value => addData(i, value), reason => reject(reason))
} else {
// 是普通对象的话, 直接返回结果
addData(i, arr[i])
}
}
resolve(resultList)
})
}
}
验证实现逻辑
let promise1 = new iPromise((resolve, reject) => {
resolve('promise的结果')
})
// 调用 iPromise.all 的静态方法
iPromise.all([1, promise1, '100']).then(value => {
console.log(value); // [1, 'promise的结果', '100']
}, reason => {
console.log(reason);
})
通过代码的返回结果可以看到,我们的实现逻辑也是没有问题的。但是如果涉及到异步操作的话,它还可以正常返回并且拿到结果吗?
含有异步操作的
let promise1 = new iPromise((resolve, reject) => {
setTimeout(() => {
console.log('延时器执行');
resolve(12)
}, 1000)
// resolve(12)
})
iPromise.all([1, promise1, '100']).then(value => {
console.log(value); // ??
}, reason => {
console.log(reason);
})
执行结果
从执行结果可以看出来,它并没有拿到延时器执行的结果,也就是说在 for 循环的内部没有等延时器执行,在循环完成后就直接返回了结果。所以在异步操作的位置获取到一个空。下面我们要在原有实现的基础上去兼容处理异步操作。
- 首先声明一个计数器用来记录每一项是否全部执行完成
- 在计数器的大小等于传入数组的 length 时,调用 resolve 方法返回结果
class iPromise {
// 通过 static 的关键字可以创建类的静态方法
static all(arr) {
// 创建一个保存所有结果的list
let resultList = []
const len = arr.length
let index = 0 // 计数器
// 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
return new iPromise((resolve, reject) => {
// 保存结果
function addData(key, value) {
resultList[key] = value
// 在每次保存结果的时候都让计数器自增
index++
// 判断当前计数器的长度, 是否等于传入数据的长度
// 如果等于则表示数组内的所有结果都已经获取到了, 调用 resolve 方法返回结果
if (index == len) {
resolve(resultList)
}
}
// 循环执行传入的回调函数获取返回结果
for (let i = 0; i < len; i++) {
// 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
if (arr[i] instanceof iPromise) {
// 执行这个 Promise 对象并获取返回结果
arr[i].then(value => addData(i, value), reason => reject(reason))
} else {
// 是普通对象的话, 直接返回结果
addData(i, arr[i])
}
}
})
}
}
验证逻辑
let promise1 = new iPromise((resolve, reject) => {
setTimeout(() => {
console.log('1s的延时器执行了');
resolve('异步的Promise')
}, 1000)
})
iPromise.all([1, promise1, '100']).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
返回结果
调用我们改写后的 all 方法可以看到,all 方法中是先执行的延时器的调用,再返回的结果,而且全部的结果页都已经获取到了。
完整版本
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class iPromise {
constructor(callback) {
// 使用 try,catch 的方式进行函数执行的异常捕获, 放置代码的阻塞
try {
callback(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
value = null
status = PENDING
reason = null
fulfilledList = []
rejectedList = []
resolve = (value) => {
if (this.status != PENDING) {
return
}
this.status = FULFILLED
this.value = value
while (this.fulfilledList.length) {
this.fulfilledList.shift()()
}
}
reject = (reason) => {
if (this.status != PENDING) {
return
}
this.status = REJECTED
this.reason = reason
while (this.rejectedList.length) {
this.rejectedList.shift()()
}
}
then(successCallback, errorCallback) {
successCallback = successCallback ? successCallback : value => value
errorCallback = errorCallback ? errorCallback : reason => reason
return new iPromise((resolve, reject) => {
if (this.status == FULFILLED) {
// 增加一个 setTimeout 的原因是把当前任务放到下一个宏任务里去执行
// 因为处理异步可能会有问题
setTimeout(() => {
try {
let result = successCallback(this.value)
isiPromise(result, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
let result = errorCallback(this.value)
isiPromise(result, resolve, reject)
} catch (error) {
reject(error)
}
})
} else {
this.fulfilledList.push(() => {
setTimeout(() => {
try {
let result = successCallback(this.value)
isiPromise(result, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.rejectedList.push(() => {
setTimeout(() => {
try {
let result = errorCallback(this.value)
isiPromise(result, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})
}
static all(arr) {
// 创建一个保存所有结果的list
let resultList = []
const len = arr.length
let index = 0
// 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
return new iPromise((resolve, reject) => {
// 保存结果
function addData(key, value) {
resultList[key] = value
index++
if (index == len) {
resolve(resultList)
}
}
// 循环执行传入的回调函数获取返回结果
for (let i = 0; i < len; i++) {
// 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
if (arr[i] instanceof iPromise) {
arr[i].then(value => addData(i, value), reason => reject(reason))
} else {
// 是普通对象的话, 直接返回结果
addData(i, arr[i])
}
}
})
}
}
function isiPromise(result, resolve, reject) {
if (result instanceof iPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
}
如有问题,敬请留言说明 ~