深入JavaScript(6)手写Promise、实现Promise的方法:all、race、allSettled、any

48 阅读7分钟

1.Promise

Promise 对象表示异步操作最终的完成(或失败)以及其结果值。

1.1 resolve和reject

let p1 = new Promise((resolve, reject) => {
 resolve('success')
 reject('fail')
})
console.log('p1', p1)
let p2 = new Promise((resolve, reject) => {
 reject('success')
 resolve('fail')
})
console.log('p2', p2)
let p3 = new Promise((resolve, reject) => {
 throw('error')
})
console.log('p3', p3)

结果如下图

image.png

这里说明Promise的四个特点

  • 执行了resolve,Promise状态会变成fulfilled;
  • 执⾏了reject,Promise状态会变成rejected;
  • Promise状态不可逆,第⼀次成功就永久为fulfilled,第⼀次失败就永远状态为rejected;
  • Promise中有throw的话,就相当于执⾏了reject;

1.1.1 实现resolve和reject

1.Promise的初始状态是pending;

2.需要对resolve和reject绑定this:确保resolve和reject的this指向永远指向当前的MyPromise实例,防⽌随着函数执⾏环境的改变⽽改变;

class MyPromise {
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        // 执行传进来的函数
        executor(this.resolve, this.reject)
    }

    initValue() {
        this.PromiseResult = null
        this.PromiseState = 'pending'
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        this.PromiseResult = value
        this.PromiseState = 'fulfilled'
    }

    reject(reason) {
        this.PromiseResult = reason
        this.PromiseState = 'rejected'
    }
}

简单测试下

image.png

1.1.2 状态不可变

image.png

正确的应该是fulfilled状态。

Promise有三种状态:

  • pending:等待中,是初始状态;
  • fulfilled:成功状态;
  • rejected:失败状态;

⼀旦状态从pending变为fulfilled或者rejected,那么此Promise实例的状态就不可以改变了。

image.png

那我们只需要在resolve和reject方法中加个判断即可;

class MyPromise {
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        // 执行传进来的函数
        executor(this.resolve, this.reject)
    }

    initValue() {
        this.PromiseResult = null
        this.PromiseState = 'pending'
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if(this.PromiseState !== 'pending') return
        this.PromiseResult = value
        this.PromiseState = 'fulfilled'
    }

    reject(reason) {
        if(this.PromiseState !== 'pending') return
        this.PromiseResult = reason
        this.PromiseState = 'rejected'
    }
}

1.1.3 throw

image.png

Promise中有throw的话,就相当于执⾏了reject。这就要使⽤try catch了,那就是在执行传入的函数的时候catch一下即可。

try {
    // 执行传进来的函数
    executor(this.resolve, this.reject)
} catch (error) {
    this.reject(error)
}

此时完整代码为:

class MyPromise {
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        try {
            // 执行传进来的函数
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.PromiseResult = null
        this.PromiseState = 'pending'
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = value
        this.PromiseState = 'fulfilled'
    }

    reject(reason) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = reason
        this.PromiseState = 'rejected'
    }
}

测试一下

image.png

1.2 then

平时then的使用一般如下:

// ⻢上输出 ”success“
const p1 = new Promise((resolve, reject) => {
    resolve('success')
}).then(res => console.log(res), err => console.log(err))
// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('fail')
    }, 1000)
}).then(res => console.log(res), err => console.log(err))
// 链式调⽤ 输出 200
const p3 = new Promise((resolve, reject) => {
    resolve(100)
}).then(res => 2 * res, err => console.log(err))
    .then(res => console.log(res), err => console.log(err))

根据上述代码可以确定:

  1. then接收两个回调,⼀个是成功回调,⼀个是失败回调;

  2. 当Promise状态为fulfilled执⾏成功回调,为rejected执⾏失败回调;

  3. 如resolve或reject在定时器⾥,则定时器结束后再执⾏then;

  4. then⽀持链式调⽤,下⼀次then执⾏受上⼀次then返回值的影响;

1.2.1 实现then

class MyPromise {
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        try {
            // 执行传进来的函数
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.PromiseResult = null
        this.PromiseState = 'pending'
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = value
        this.PromiseState = 'fulfilled'
    }

    reject(reason) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = reason
        this.PromiseState = 'rejected'
    }

    then(onFulfilled, onRejected) {
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

        if (this.PromiseState === 'fulfilled') {
            onFulfilled(this.PromiseResult)
        } else if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult)
        }
    }
}

测试一下

image.png

1.2.2 定时器

怎么保证下述代码能够在1s后执行then的回调?

// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('fail')
    }, 1000)
}).then(res => console.log(res), err => console.log(err))

我们不能确保1秒后才执⾏then函数,但是我们可以保证1秒后再执⾏then⾥的回调

image.png

在这1秒时间内,我们可以先把then⾥的两个回调保存起来,然后等到1秒过后,执⾏了resolve或者reject,咱们再去判断状态,并且判断要去执⾏刚刚保存的两个回调中的哪⼀个回调。那么问题来了,我们怎么知道当前1秒还没⾛完甚⾄还没开始⾛呢?其实很好判断,只要状态是pending,那就证明定时器还没跑完,因为如果定时器跑完的话,那状态肯定就不是pending,⽽是fulfilled或者rejected

那是⽤什么来保存这些回调呢?建议使⽤数组,因为⼀个promise实例可能会多次then,⽤数组就⼀个⼀个保存了

现在代码为:

class MyPromise {
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        try {
            // 执行传进来的函数
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.PromiseResult = null
        this.PromiseState = 'pending'
        this.onFulfilledCallbacks = [] // 保存成功回调
        this.onRejectedCallbacks = [] // 保存失败回调
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = value
        this.PromiseState = 'fulfilled'
        while (this.onFulfilledCallbacks.length) {
            this.onFulfilledCallbacks.shift()(this.PromiseResult)
        }
    }

    reject(reason) {
        if (this.PromiseState !== 'pending') return
        this.PromiseResult = reason
        this.PromiseState = 'rejected'
        while (this.onRejectedCallbacks.length) {
            this.onRejectedCallbacks.shift()(this.PromiseResult)
        }
    }

    then(onFulfilled, onRejected) {
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

        if (this.PromiseState === 'fulfilled') {
            onFulfilled(this.PromiseResult)
        } else if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult)
        } else if (this.PromiseState === 'pending') {
            this.onFulfilledCallbacks.push(onFulfilled.bind(this))
            this.onRejectedCallbacks.push(onRejected.bind(this))
        }
    }
}

看下是否能够实现定时器的功能:

image.png

1.2.3 链式调用

then⽀持链式调⽤,下⼀次then执⾏受上⼀次then返回值的影响,给⼤家举个例⼦:

// 链式调⽤ 输出 200
const p3 = new Promise((resolve, reject) => {
    resolve(100)
}).then(res => 2 * res, err => console.log(err))
    .then(res => console.log(res), err => console.log(err))
    
// 链式调⽤ 输出300
const p4 = new Promise((resolve, reject) => {
    resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err =>
    console.log(err))
    .then(res => console.log(res), err => console.log(err))

根据上⽂,可以得到:

  1. then⽅法本身会返回⼀个新的Promise对象;

  2. 如果返回值是promise对象,返回值为成功,新promise就是成功;

  3. 如果返回值是promise对象,返回值为失败,新promise就是失败;

  4. 如果返回值⾮promise对象,新promise对象就是成功,值为此返回值;

then是Promise上的⽅法,那如何实现then完还能再then呢?

then执⾏后返回⼀个Promise对象就⾏了,就能保证then完还能继续执⾏then;

image.png

then(onFulfilled, onRejected) {
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

        var thenPromise = new MyPromise((resolve, reject) => {
            const resolvePromise = cb => {
                try {
                    const x = cb(this.PromiseResult)
                    if (x === thenPromise && x) { throw new Error('不能返回自身。') }
                    if (x instanceof MyPromise) {
                        // 返回值是Promise
                        x.then(resolve, reject)
                    } else {
                        resolve(x)
                    }
                } catch (error) {
                    reject(error)
                    throw new Error(error)
                }
            }

            if (this.PromiseState === 'fulfilled') {
                resolvePromise(onFulfilled)
            } else if (this.PromiseState === 'rejected') {
                resolvePromise(onRejected)
            } else if (this.PromiseState === 'pending') {
                this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
                this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
            }
        })

        return thenPromise
    }

完整代码为:

class MyPromise {
    // 构造⽅法
    constructor(executor) {
        // 初始化值
        this.initValue()
        // 初始化this指向
        this.initBind()
        try {
            // 执⾏传进来的函数
            executor(this.resolve, this.reject)
        } catch (e) {
            // 捕捉到错误直接执⾏reject
            this.reject(e)
        }
    }
    initBind() {
        // 初始化this
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    initValue() {
        // 初始化值
        this.PromiseResult = null // 终值
        this.PromiseState = 'pending' // 状态
        this.onFulfilledCallbacks = [] // 保存成功回调
        this.onRejectedCallbacks = [] // 保存失败回调
    }
    resolve(value) {
        // state是不可变的
        if (this.PromiseState !== 'pending') return
        // 如果执⾏resolve,状态变为fulfilled
        this.PromiseState = 'fulfilled'
        // 终值为传进来的值
        this.PromiseResult = value
        // 执⾏保存的成功回调
        while (this.onFulfilledCallbacks.length) {
            this.onFulfilledCallbacks.shift()(this.PromiseResult)
        }
    }
    reject(reason) {
        // state是不可变的
        if (this.PromiseState !== 'pending') return
        // 如果执⾏reject,状态变为rejected
        this.PromiseState = 'rejected'
        // 终值为传进来的reason
        this.PromiseResult = reason
        // 执⾏保存的失败回调
        while (this.onRejectedCallbacks.length) {
            this.onRejectedCallbacks.shift()(this.PromiseResult)
        }
    }
    then(onFulfilled, onRejected) {
        // 接收两个回调 onFulfilled, onRejected
        // 参数校验,确保⼀定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        var thenPromise = new MyPromise((resolve, reject) => {
            const resolvePromise = cb => {
                try {
                    const x = cb(this.PromiseResult)
                    if (x === thenPromise) {
                        // 不能返回⾃身哦
                        throw new Error('不能返回⾃身。。。')
                    }
                    if (x instanceof MyPromise && x) {
                        // 如果返回值是Promise
                        // 如果返回值是promise对象,返回值为成功,新promise就是成功
                        // 如果返回值是promise对象,返回值为失败,新promise就是失败
                        // 谁知道返回的promise是失败成功?只有then知道
                        x.then(resolve, reject)
                    } else {
                        // ⾮Promise就直接成功
                        resolve(x)
                    }
                } catch (err) {
                    // 处理报错
                    reject(err)
                    throw new Error(err)
                }
            }
            if (this.PromiseState === 'fulfilled') {
                // 如果当前为成功状态,执⾏第⼀个回调
                resolvePromise(onFulfilled)
            } else if (this.PromiseState === 'rejected') {
                // 如果当前为失败状态,执⾏第⼆个回调
                resolvePromise(onRejected)
            } else if (this.PromiseState === 'pending') {
                // 如果状态为待定状态,暂时保存两个回调
                // 如果状态为待定状态,暂时保存两个回调
                this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
                this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
            }
        })
        // 返回这个包装的Promise
        return thenPromise
    }
}

测试一下:

image.png

1.3 其他方法

1.3.1 all

  1. 接收⼀个Promise数组,数组中如有⾮Promise项,则此项当做成功;

  2. 如果所有Promise都成功,则返回成功结果数组;

  3. 如果有⼀个Promise失败,则返回这个失败结果;

static all(promise) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) => {
            const addData = (index, value) => {
                result[index] = value
                count++
                if (count === promise.length) resolve(result)
            }

            promise.forEach((promise, index) => {
                if (promise instanceof MyPromise) {
                    promise.then(res => {
                        addData(index, res)
                    }, err => reject(err))
                } else {
                    addData(index, promise)
                }
            });
        })
    }

1.3.2 race

  1. 接收⼀个Promise数组,数组中如有⾮Promise项,则此项当做成功;

  2. 哪个Promise最快得到结果,就返回那个结果,⽆论成功失败;

static race(promise) {
        return new MyPromise((resolve, reject) => {
            promise.forEach(item => {
                if (item instanceof MyPromise) {
                    item.then(res => {
                        resolve(res)
                    }, err => {
                        reject(err)
                    })
                } else {
                    resolve(item)
                }
            })
        })
    }

1.3.3 allSettled

  1. 接收⼀个Promise数组,数组中如有⾮Promise项,则此项当做成功;

  2. 把每⼀个Promise的结果,集合成数组后返回;

static allSettled(promises) {
        return new MyPromise((resolve, reject) => {
            const result = []
            let count = 0
            const addData = (status, value, index) => {
                result[index] = {
                    status,
                    value
                }
                count++
                if (count === promises.length) resolve(result)
            }

            promises.forEach((item, index) => {
                if (promises instanceof MyPromise) {
                    item.then(res => {
                        addData('fulfilled', res, index)
                    }, err => {
                        addData('rejected', err, index)
                    })
                } else {
                    addData('fulfilled', item, index)
                }
            })
        })
    }

1.3.4 any

与all相反

  1. 接收⼀个Promise数组,数组中如有⾮Promise项,则此项当做成功;

  2. 如果有⼀个Promise成功,则返回这个成功结果;

  3. 如果所有Promise都失败,则报错;

static any(promises) {
    let count = 0
    return new MyPromise((resolve, reject) => {
        promises.forEach(item => {
            if (item instanceof MyPromise) {
                item.then(res => {
                    resolve(res)
                }, err => {
                    count++
                    if (count === promises.length) reject('error')
                })
            }
            else {
                resolve(item)
            }
        })
    })
}