手写Promise的静态方法

277 阅读6分钟

众所周知,Promise是es6新增的非常优雅的解决异步的一种方法,在它身上,还有许多非常好用的静态方法。今天我们来一一手写一下这些常用的静态方法,搞懂原理总是好过死记硬背。

1. Promise.resolve

首先就是resolve方法,它会接收一个值,总是会将其转换为一个状态为fulfilled的Promise对象,如果传入的值本身就是Promise对象,那就直接返回。

了解原理代码便呼之欲出了:

Promise.resolve = function (value) {
    if (value instanceof Promise) {
        return value; // 当传入的是一个Promise对象时,直接返回
    }
    return new Promise(resolve => resolve(value)) // 否则返回一个状态为fulfilled的Promise
}

先对传入的value进行类型判断,使用instanceof进行判断。instanceof是通过原型链进行类型判断的,所以当value是Promise对象时,就会得到true,直接返回value即可;如果value不是Promise对象,那就返回一个新的Promise对象并且使用resolve包裹一下,就行了。

2. Promise.reject

reject方法就是与resolve方法相反的,总是会得到一个状态为rejected的promise对象,不一样的是,不管传入的value是什么,都返回一个状态为rejected的promise对象:

Promise.reject = function (value) {
    // 不管传入的是什么,直接返回一个状态为rejected的promise
    return new Promise((resolve, reject) => reject(value)) 
}

3. Promise.all

然后是all方法。

all方法会接收一个由Promise对象组成的数组,如果数组中的Promise对象状态都是fufilled,返回一个由这些Promise对象的值组成的新的状态为fufilled的promise;只要有一个失败就立即失败,返回一个状态为rejected的Promise,值为第一个失败的promise对象的值。

了解了原理,我们就来手写代码:

Promise.all = function (PromiseArr) {
    let i = 0, result = [] // i用来记录PromiseArr中成功的promise对象,result用来保存promise对象的值
    return new Promise((resolve, reject) => { // all方法会返回一个新的promise对象
        
    })
}

我们会使用一个i来记录PromiseArr中成功的promise对象个数,每成功一个i就加一,便把这个promise对象的值存放到result数组中。然后all最后一定会返回一个新的Promise,于是我们return new Promise。

Promise.all = function (PromiseArr) {
    let i = 0, result = [] // i用来记录PromiseArr中成功的promise对象,result用来保存promise对象的值
    return new Promise((resolve, reject) => { // all方法会返回一个新的promise对象
        PromiseArr.forEach((item, index) => { // 遍历传入的promise对象数组
            Promise.resolve(item)
        })
    })
}

然后我们遍历传入的PromiseArr数组,使用resolve方法将PromiseArr数组中的每个元素都转换为promise。如果item是promise,resolve方法会直接返回它;如果不是,返回一个promise对象。

于是,我们就可以在后面接then了:

Promise.all = function (PromiseArr) {
    let i = 0, result = [] // i用来记录PromiseArr中成功的promise对象,result用来保存promise对象的值
    return new Promise((resolve, reject) => { // all方法会返回一个新的promise对象
        PromiseArr.forEach((item, index) => { // 遍历传入的promise对象数组
            Promise.resolve(item).then(
                val => { // 利用resolve方法将promise数组中的元素都变为promise
                    i++ // 每成功一个就加1
                    result[index] = val // 按索引记录值
                    if (i === PromiseArr.length) { // 当index等于promise数组长度时,说明全部成功,直接resolve
                        resolve(result)
                    }
                },
                err => {
                    reject(err) // 只要有一个失败,立即失败
                })
        })
    })
}

当item状态是fufilled时,i就加一,并根据索引将item的值存放到result中,最后当i的值等于PromiseArr数组的长度时,说明PromiseArr数组每个promise都成功了,就resolve这个result数组;否则,只要有一个失败,就立即reject。

这样,一个基本的all方法我们就实现了。

4. Promise.any

any方法和all方法比较类似。any方法是当传入的promise数组每个状态都为rejected时返回一个状态为rejected的新Promise;否则,只要有一个状态为fufilled,就立即成功,返回第一个是 fulfilled 的新实例。

Promise.any = function (PromiseArr) {
    let i = 0 // 用来记录PromiseArr中失败的个数
    return new Promise((resolve, reject) => { // any方法会返回一个新的promise对象
        
    })
}

开头和all方法比较类似,我们使用一个i来记录PromiseArr数组中失败的个数,然后any方法会返回一个新的promise对象,于是我们就return new Promise。

Promise.any = function (PromiseArr) {
    let i = 0 // 用来记录PromiseArr中失败的个数
    return new Promise((resolve, reject) => { // any方法会返回一个新的promise对象
        PromiseArr.forEach((item, index) => { // 遍历PromiseArr数组
            Promise.resolve(item)
        })
    })
}

同样,我们使用resolve方法将数组中的每一项item都转化为promise对象。

Promise.any = function (PromiseArr) {
    let i = 0 // 用来记录PromiseArr中失败的个数
    return new Promise((resolve, reject) => { // any方法会返回一个新的promise对象
        PromiseArr.forEach((item, index) => { // 遍历PromiseArr数组
            Promise.resolve(item).then(
                val => { // 利用resolve方法将PromiseArr数组中的每个元素都变为promise对象
                    resolve(val) // 只要有一个成功,立即成功
                },
                err => {
                    i++ // 记录失败的次数
                    if (i === PromiseArr.length) { // 当数组中全部失败时
                        reject(new AggregateError('All promises were rejected'))
                    }
                })
        })
    })
}

然后,每失败一次,i就加1。最后,当i等于PromiseArr数组的长度时,说明全部失败,就reject,值为 AggregateError 的错误;否则,只要有一个成功,立即成功。

这样,我们就实现了一个基本的any方法。

5. Promise.race

我们紧接着来手写一下race方法,race方法的原理是会返回第一个得出状态的promise的值,无论成功还是失败。

原理很简单,代码也比较简单:

Promise.race = function (PromiseArr) {
    return new Promise((resolve, reject) => {  // race方法会返回一个新的promise对象
        PromiseArr.forEach((item, index) => { // 遍历处理PromiseArr中元素
            Promise.resolve(item).then(
                val => {
                    resolve(val) // 返回第一个成功的
                },
                err => {
                    reject(err) // 或者第一个失败的
                })
        })
    })
}

6. Promise.allSettled

我们来手写一下今天的最后一个静态方法,也是比较复杂的一个,allSettled。

它主要是用来观察一组promise对象数组发生的变化,所有 Promise 的状态都变化了,那么新返回一个状态是 fulfilled 的 Promise,且它的值是一个数组,数组的每项由所有 Promise 的值和状态组成的对象;

Promise.allSettled = function (PromiseArr) {
    let result = []
    return new Promise((resolve, reject) => {
       
    })
}

我们使用result数组来保存最后的结果,allSettled方法也会返回一个新的promise对象,于是我们return new Promise。

Promise.allSettled = function (PromiseArr) {
    let result = []
    return new Promise((resolve, reject) => {
        PromiseArr.forEach((item, index) => {
            Promise.resolve(item)
        })
    })
}

同样,使用resolve方法将数组的每一项转换为promise对象。

Promise.allSettled = function (PromiseArr) {
    let result = []
    return new Promise((resolve, reject) => {
        PromiseArr.forEach((item, index) => {
            Promise.resolve(item).then(
                val => {
                    result.push({
                        status: 'fulfilled',
                        value: val,
                    })
                    if (result.length === PromiseArr.length) {
                        resolve(result)
                    }
                },
                err => {
                    result.push({
                        status: 'rejected',
                        reason: err,
                    })
                    if (result.length === PromiseArr.length) {
                        resolve(result)
                    }
                })
        })
    })
}

我们将每一个item的值都存入到result数组中,并且要记录每个item的状态,allSettled总是返回一个状态为fufilled的新promise,最后,当result的长度等于PromiseArr的长度,说明处理完毕,直接resolve这个result数组。

这样,我们也实现了一个基本的allSettled方法。

7. 总结

今天我们主要学习了一下promise身上一些常用的静态方法是如何实现的。最后,如果对你有帮助的话请点个赞吧!