阅读 63

如何手动实现一个Promise.all

我相信有些同学在面试的过程中,面试官会问你能否简单描述一下Promise.all的原理,小编我也是被问到,于是也在网上找了几遍文章和例子,看完之后也是死恍然大悟,希望能帮到小伙伴们

Promise.all原理

Promise.all 接收一个 promise 对象的数组作为参数,当这个数组里的所有 promise 对象全部变为resolve或 有 reject 状态出现的时候,它才会去调用 .then 方法,它们是并发执行的

在实现之前先解读promise.all 的特点

1、Promise.all()方法将多个Promise实例包装成一个Promise对象(p),接受一个数组(p1,p2,p3)作为参数,数组中不一定需要都是Promise对象,但是一定具有Iterator接口,如果不是的话,就会调用Promise.resolve将其转化为Promise对象之后再进行处理。

2、使用Promise.all()生成的Promise对象(p)的状态是由数组中的Promise对象(p1,p2,p3)决定的;

  • 1、如果所有的Promise对象(p1,p2,p3)都变成fullfilled状态的话,生成的Promise对象(p)也会变成fullfilled状态,p1,p2,p3三个Promise对象产生的结果会组成一个数组返回给传递给p的回调函数;
  • 2、如果p1,p2,p3中有一个Promise对象变为rejected状态的话,p也会变成rejected状态,第一个被rejected的对象的返回值会传递给p的回调函数。

Promise.all()方法生成的Promise对象也会有一个catch方法来捕获错误处理,但是如果数组中的Promise对象变成rejected状态时,并且这个对象还定义了catch的方法,那么rejected的对象会执行自己的catch方法,并且返回一个状态为fullfilled的Promise对象,Promise.all()生成的对象会接受这个Promise对象,不会返回rejected状态

版本一

function PromiseAll(arr) {
    //PromiseAll的返回值为一个promise对象
    return new Promise((resolve, reject) => {
        //PromiseAll的入参必须是数组
        if (!Array.isArray(arr)) {
            return reject(new TypeError('arr must be an array.'));
        }
        let resArr = [];
        for (let i in arr) {
            (function(i) {
                Promise.resolve(arr[i]).then(res => {
                    resArr.push(res);
                    //只有所有的都成功了,才会返回resolve
                    if (i == arr.length - 1) {
                        return resolve(resArr);
                    }
                }, err => {
		    // 只要出错就抛出
                    return reject(err)
                }).catch(err => {
                    console.log(err)
                })
            })(i)
        }
    })
}

//测试
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
  console.log(res) // 3秒之后打印 ["1", "2", "3"]
)
.catch((e) => {
  console.log(e)
})
复制代码

版本二

function  PromiseAll(promiseArrs) {
  return new Promise((resolve, reject) => { //返回一个新的Promise
    let arr = []; //定义一个空数组存放结果
    let i = 0;
    function handleData(index, data) { //处理数据函数
        arr[index] = data;
        i++;
        if (i === promiseArrs.length) { //当i等于传递的数组的长度时 
            resolve(arr); //执行resolve,并将结果放入
        }
    }
    for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
        promiseArrs[i].then((data) => {
            handleData(i, data); //将结果和索引传入handleData函数
        }, reject)
    }
 })
}

// 测试
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
 console.log(res) // 3秒之后打印 ["1", "2", "3"]
)
.catch((e) => {
 console.log(e)
})
复制代码

版本三

function  PromiseAll(promiseArray) {
  return new Promise(function(resolve,reject){
    //判断参数类型
    if(!Array.isArray(promiseArray)){
        return reject(new TypeError('arguments muse be an array'))
    }
    var counter = 0,
        promiseNum = promiseArray.length,
        resolvedArray = new Array(promiseNum);
    for(var i = 0; i < promiseNum; i++){
        (function(i){
            Promise.resolve(promiseArray[i]).then(function(value){
                counter++;
                resolvedArray[i] = value;
                if(counter == promiseNum){
                    return resolve(resolvedArray)
                }
            },function(reason){
                return reject(reason)
            })
        })(i)
    }
 })
}

// 测试
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
 console.log(res) // 3秒之后打印 ["1", "2", "3"]
)
.catch((e) => {
 console.log(e)
})
复制代码

总结

不管版本如何变动,只要围绕Promise.all几个特点解读就可以

  1. Promise.all()接受一个 Array 类型的参数
  2. 只有数组中全部的 Promise 都变为 resolve 的时候
  3. 返回一个新的 Promise 实例
  4. 只要有一个失败,状态就变成 rejected

更多关于Promise.all文章

介绍下 Promise.all 使用、原理实现及错误处理
手动实现一个 Promise.all()
实现promise.all方法

关于我

关注下面的标签,发现更多相似文章
评论