一、手写 Promise.all:
思路:
1. 首先 Promise.all返回的肯定是一个 promise对象,所以可以直接写 return new Promise((resolve, reject) => {});
2. 遍历传入的参数,用 Promise.resolve() 将参数的每一项进行一个包裹,使其变成一个 promise 对象,因为传入的参数不一定是 Promise 类型,可能不存在 then 方法;
3. 关键点是何时 resolve,可以声明一个变量,进行计数,当在每个 promise 对象 then 之后将 res放入到 result 中后,就可以将计数器 +1,当 +1 后与传入的参数的长度进行比较,如果相等就可以resolve了;
4. 如果期间有报错,就直接 reject 掉,后面的不再执行了,因为 Promise.all 就是所有都 resolve 才可以;
5. 官方规定Promise.all 接受的参数是一个可遍历的参数,所以未必是一个数组,可以用 Array.from() 转化一下;
6. 也可以不转化,直接使用for..of 进行遍历,因为凡是可遍历的变量应该都部署了itertetor方法,如果不转化的话,无法使用 for..in 进行遍历,所以用 for..of 遍历最安全。
引申:我们知道 Iterator 的接口就是提供了一个统一的访问机制,如果我们访问一个对象使用 Iterator API,需要不断的调用 next() 才能遍历完成;如果 Iterator 像 java那样提供一个hasNext()方法的话,那么我们可以通过while进行遍历,事实上 js 中是没有的。
之所以没有是因为ES6使用for...of...实现了对具有Symbol.iterator(可遍历)的数据结构的遍历,也就是说只要是包含Symbol.iterator属性的结构都可以使用for...of...进行遍历。
代码实现:
function promiseAll (args) {
return new Promise((resolve, reject) => {
const result = [];
let iteratorIndex = 0;
let fullCount = 0;
for(const item of args) {
let resultIndex = iteratorIndex;
iteratorIndex += 1;
Promise.resolve(item).then(res => {
result[resultIndex] = res;
fullCount += 1;
if(fullCount == iteratorIndex) {
resolve(result)
}
}).catch(err => {reject(err)})
}
if(iteratorIndex === 0) {
resolve(result)
}
})
}
if(!Promise.all) Promise.all = promiseAll
简易有瑕疵版:
Promise.prototype.all = function(promises) {
let results = [];
let promiseCount = 0;
let promisesLength = promises.length;
return new Promise((resolve, reject) => {
for(let i in promises) {
Promise.resolve(promises[i]).then(res => {
results[i] = res;
if(++promiseCount === promisesLength) {
resolve(results) }
}).catch(err => {
reject(err)
})
}
})
}
二、手写Promise.race
因为Promise.race是只要有人能决议就返回谁,所以只要将all的计数器和逻辑判断全部去掉就好了
代码实现:
Promise.prototype.race = function(args) {
return new Promise((resolve, reject) => {
for(const p of args) {
Promise.resolve(p).then(res => {
resolve(res);
}).catch(err => reject(err))
}
})
}