Promise:all、finally、resolve、race、catch的实现

556 阅读4分钟

Promise.all

根据上一篇博客中自己写的 Promise 完成 all 方法的实现,all 就是全部成功就 resolve,只要有一个抢先失败,就执行 reject。

由于自己实现的 Promise 与原生的不太一样,比如原生的 Promise.resolve 能解析普通值为 Promise。所以这里我们自己写一个是否为 Promise 函数来判断。具体代码如下:

//解决异步并发,同步处理结果
let Promise = require('./promise')
let fs = require('fs')

function read(url) {
    let dfd = Promise.defer()
    fs.readFile(url, 'utf8', function (err, data) {
        if (err) dfd.reject(err)
        dfd.resolve(data)
    })
    return dfd.promise
}

function isPromise(x) {
    if (typeof x === 'object' && x !== null || typeof x === 'function') {
        try {
            let then = x.then //当前有then方法,姑且认为 x 是个Promise
            if (typeof then === 'function') {
                return true
            }
        } catch (err) {
            return false
        }
    } else {
        return false
    }
}

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let len = promises.length
        let arr = new Array(len)
        let count = 0

        function processData(i, data) {
            arr[i] = data
            count++
            if (count === len) {
                resolve(arr)
            }
        }

        for (let i = 0; i < len; i++) {
            current = promises[i]
            if (isPromise(current)) {
                current.then(data => {
                    processData(i, data)
                }, err => {
                    reject(err)
                })
            } else {
                processData(i, current)
            }
        }
    })
}

Promise.all([1, 2, 3, read('./name.txt'), read('./age.txt'), 6, 7]).then(data => {
    console.log(data)
}, err => {
    console.log(err)
})

基于原生 Promise 的写法如下

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let len = promises.length
        let fulfilledResult = new Array(len)
        let count = 0
        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(data => {
                fulfilledResult[index] = data
                count++
                if (count === len) {
                    resolve(fulfilledResult)
                }
            }, error => {
                reject(error)
            })
        });
    })
}

Promise.prototype.finally

finally 的实现基于原生的 Promise。 finally 的特点是不管当前的 Promise 是成功还是失败,都会执行它接收的回调函数,对后面的 then 的使用无影响,也就是会发生值穿透,外加执行回调函数。

以下面代码为例,

let p = new Promise((resolve, reject) => {
    resolve(1)
    // reject(2)
})
p.finally(() => { //无论成功失败都执行,并且将状态进行传递
    console.log('最终的')
}).then(data => {
    console.log(data)
}).catch(err => {
    console.log(err)
})

执行结果是 原理就是需要返回一个 Promise,这个新的 Promise 与当前 Promise 的状态一致,且成功值或者失败值一致。

回忆一下 then 方法的值穿透本质,我们只需要在穿透的中间,不管成功还是失败,执行一下 finally 接收的回调函数即可,代码如下

Promise.prototype.finally = function (cb) {
    return this.then(data => {
        cb()
        return data
    }, err => {
        cb()
        throw err
    })
}

如果 finally 接收的回调函数中返回一个 Promise,而且其执行器中有异步代码,比如

let p = new Promise((resolve, reject) => {
    resolve(1)
    // reject(2)
})
p.finally(() => { //无论成功失败都执行,并且将状态进行传递
    console.log('最终的')
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 4000)
    })
}).then(data => {
    console.log(data)
}).catch(err => {
    console.log(err)
})

我们会发现原生的 Promise会等这个 Promise 执行完之后才会走下面的 then,这里我们要用到 Promise.resolve 去解析,先给出代码

Promise.finally = function (cb) {
    return this.then(data => {
        return Promise.resolve(cb()).then(() => data)
    }, err => {
        return Promise.resolve(cb()).then(() => {
            throw err
        })
    })
}

这里为什么两层 return 和 then 呢?

  1. 外层的 return 为了返回一个值穿透的 Promise
  2. 如果 cb() 中返回 Promise, 内层的 Primise.resolve 会等待 cb()执行完,并解析这个 Promise,所以内层的 return 不仅能进行值穿透还能等到 cb执行完。
  3. 同时需要发生值穿透,使用 .then(() => data),利用闭包将 data 向后传递,这个 then 中返回值会被解析,本身是成功就成功,本身是失败就失败,所以依然是值穿透,也就是值经过了两层 then 的值穿透。

另外,mdn 给出一条注意:在finally回调中 throw(或返回被拒绝的promise)将以 throw() 指定的原因拒绝新的promise。只需要稍微修改一下即可:

Promise.finally = function (cb) {
    return this.then(data => {
        return Promise.resolve(cb()).then(() => data)
    }, err => {
        return Promise.resolve(cb()).then(() => {
            throw err
        }, err2 => {
            throw err2
        })
    })
}

Promise.resolve

如果接收的参数是普通值,会把这个普通值作为成功值并包装成成功态的 Promise。如果接收的是 Promise,会等待 Promise 执行完之后再继续执行,其状态还是这个 Promise 的状态,可以认为是等待执行 + 值穿透。 简单测试下是否如此 成功的 Promise:

let p = new Promise((resolve, reject) => {
    resolve(1)
})
Promise.resolve(p).then(data => {
    console.log('success:' + data)
}, err => {
    console.log('error:' + err)
})

结果为 失败的 Promise:

let p = new Promise((resolve, reject) => {
    reject(1)
})
Promise.resolve(p).then(data => {
    console.log('success:' + data)
}, err => {
    console.log('error:' + err)
})

结果为

普通值:

Promise.resolve(1).then(data => {
    console.log('success:' + data)
}, err => {
    console.log('error:' + err)
})

好了,了解了基本特性之后,我们就可以简单实现自己的 resolve 了

Promise.resolve = function (value) {
    return Promise((resolve, reject) => {
        if (value instanceof Promise) {
            value.then(data => {
                resolve(data)
            }, err => {
                reject(err)
            })
        } else {
            resolve(value)
        }
    })
}

如果接收的 Promise 的 resolve 接收的参数也是一个 Promise,那么需要再递归解析,这里就不实现了,这里的思路与上一篇博客中的 resolvePromise 的实现基本是一样的。

Promise.race

比较简单,就不介绍了,直接上代码

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        promises.forEach(promise => {
            promise.then(data => {
                resolve(data)
            }, err => {
                reject(err)
            })
        })
    })
}

Promise.catch

Promise.catch 是 .then(null, rejection) 或 .then(undefined, rejection) 的别名

Promise.catch = function(onRejected){
    return this.then(undefined,onRejected)
}