前端异步编程(1)Promise

50 阅读4分钟

1.Promise的基本用法

传统的异步处理的方式通常是通过回调函数去实现的,或者发布订阅 方式的事件监听。但是Promise出现之后就能够方便的进行异步处理。

先看Promise的基本用法:

const fn = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    },1000)
})
fn.then((res) => {
    console.log(res)
})

在实例化Promise的时候,传进去了一个函数,这个函数有两个参数resolve和reject,它们是可执行函数,能够在then方法中去获取到resolve和reject执行时传入的参数。

2.初始化

首先写一个类,执行传入的函数executor,初始化状态为pending

class MyPromise {
    constructor(executor){
        this.initValue()
        executor(this.resolve, this.reject)
    }
    initValue(){
        this.PromiseState = 'pending'
        this.PrimiseResult = null
    }
    resolve(value){
        if(this.PromiseState !== 'pending') return
        this.PromiseState = "fulfilled"
        this.PromiseResult = value
    }
    reject(value){
        if(this.PromiseState !== 'pending') return
        this.PromiseState = 'rejected'
        this.PromiseResult = value
    }
}

接下来尝试去执行代码:

const fn = new MyPromise((resolve, reject) => {
    resolve(1)
})
console.log(fn)

会发现报错了。Uncaught TypeError: Cannot read properties of undefined (reading 'PromiseState'),执行到resolve的时候,this为undefind。所以在初始化的时候要绑定一下this的指向

class MyPromise {
    constructor(executor){
        this.initValue()
        this.initBind()
        executor(this.resolve, this.reject)
    }
    initValue(){
        this.PromiseState = 'pending'
        this.PromiseState = null
    }
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    resolve(value){
        console.log(this)
        if(this.PromiseState !== 'pending') return
        this.PromiseState = "fulfilled"
        this.PromiseResult = value
    }
    reject(reason){
        if(this.PromiseState !== 'pending') return
        this.PromiseState = 'rejected'
        this.PromiseResult = reason
    }
}

在执行的过程中如果遇到了throw的情况时

const fn = new Promise((resolve, reject) => {
    throw("出错啦")
})
console.log(fn)

image.png promise要捕获该异常,执行返回错误原因,状态要变更为rejected,所以就可以通过try catch实现代码实现如下

    constructor(executor){
        this.initValue()
        this.initBind()
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

3. then

3.1 实现then的基本操作

const fn = new Promise((resolve, reject) => {
    //resolve(1)
    //reject(2)
})
fn.then(res => {
    console.log(res)
},err => {
    console.log(err)
})
console.log(fn)

它有两个参数,都为函数,它们的参数就是reject和resolve的返回结果。代码实现如下


class MyPromise {
    ...
    // 根据状态来决定执行哪个函数
    then(onFulfilled,onRejected){
            // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

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

定时器的情况

const fn = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    },1000)
})
fn.then(res => {
    console.log(res)
},err => {
    console.log(err)
})

如果执行上面的代码,会发现then中是不会打印的,原因就是 PromiseState仍然是pending,要间隔1秒它的状态才会变化。 解决办法:当状态为pending时,将onFulfilled,onRejected存起来。当 状态变为 fulfilled或rejected时才去执行


class MyPromise {
    ...
    initValue(){
        this.PromiseState = 'pending'
        this.PromiseResult = null
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
    }

    resolve(value){
        ...
        while(this.onFulfilledCallbacks.length){
            this.onFulfilledCallbacks.shift()(this.PromiseResult)
        }

    }
    reject(reason){
        ...
        while(this.onFulfilledCallbacks.length){
            this.onRejectedCallbacks.shift()(this.PromiseResult)
        }
    }
    then(onFulfilled,onRejected){
        ...
        if(this.PromiseState === 'fulfilled'){
            onFulfilled(this.PromiseResult)
        }else if(this.PromiseState === 'rejected'){
            onRejected(this.PromiseResult)
        }else if(this.PromiseState === 'pending'){
            this.onFulfilledCallbacks.push(onFulfilled)
            this.onRejectedCallbacks.push(onRejected)
        }
    }
}

3.2 链式调用

要实现链式调用,那么then方法必然需要继续返回一个promise才可以。

    then(onFulfilled,onRejected){
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        
        const thenPromise = new MyPromise((resolve,reject) => {
            if(this.PromiseState === 'fulfilled'){
                onFulfilled(this.PromiseResult)
            }else if(this.PromiseState === 'rejected'){
                onRejected(this.PromiseResult)
            }else if(this.PromiseState === 'pending'){
                this.onFulfilledCallbacks.push(onFulfilled)
                this.onRejectedCallbacks.push(onRejected)
            }
        })

        return thenPromise
    }
const fn = new MyPromise((resolve, reject) => {
        resolve(1)
})
fn.then(res => {
    console.log(res)
    return res
},err => {
    console.log(err)
}).then(res1 => {
    console.log(res1)
})

这样虽然返回了一个promise,但是并没有去执行resolve,所以第二个res1 并没有被打印,所以要去执行resolve

    then(onFulfilled,onRejected){
        debugger
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        const thenPromise = new MyPromise((resolve,reject) => {
            if(this.PromiseState === 'fulfilled'){
                resolve(onFulfilled(this.PromiseResult)) 
            }else if(this.PromiseState === 'rejected'){
                reject(onRejected(this.PromiseResult))
            }else if(this.PromiseState === 'pending'){
                this.onFulfilledCallbacks.push(onFulfilled)
                this.onRejectedCallbacks.push(onRejected)
            }
        })

        return thenPromise
    }

这样就能就能打印两次1了。 看着好像没问题了,但是如果上一次的then方法继续返回的是一个promise对象,代码如下

const fn1 = new Promise((resolve, reject) => {
    resolve(1)
})
fn.then(res => {
    console.log(res)
    return new Promise((resolve,reject) => {
        resolve(res)
    })
},err => {
    console.log(err)
}).then(res1 => {
    console.log(res1)
})

它打印出了1,所以上一次返回的是一个promise要主动的去执行then方法,所以改造自己的方法

    then(onFulfilled,onRejected){
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        
        const resolvePromise = (cb) => {
            let x = cb(this.PromiseResult)
            if(x instanceof MyPromise){
                x.then(resolve, reject)
            }else{
                resolve(x)
            }
        }
        const thenPromise = new MyPromise((resolve,reject) => {
            if(this.PromiseState === 'fulfilled'){
                resolvePromise(onFulfilled)
            }else if(this.PromiseState === 'rejected'){
                resolvePromise(onRejected)
            }else if(this.PromiseState === 'pending'){
                this.onFulfilledCallbacks.push.bind(this,onFulfilled)
                this.onRejectedCallbacks.push.bind(this,onRejected)
            }
        })

        return thenPromise
    }

这里要注意this 的指向问题

另外如果上一个then 方法出错,那么需要通过try catch 去捕获异常,并直接 reject

        const thenPromise = new MyPromise((resolve,reject) => {
            const resolvePromise = (cb) => {
                // setTimeout(() => {
                try {
                    console.log(this)
                    let x = cb(this.PromiseResult)
                    if(x instanceof MyPromise){
                        // x.then(value => resolve(value),err => reject(err))
                        x.then(resolve, reject)
                    }else{
                        resolve(x)
                    }
                } catch (error) {
                    reject(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))
            }
        })

这样完整的代码就实现了:

class MyPromise {
    constructor(executor){
        this.initValue()
        this.initBind()
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    initValue(){
        this.PromiseState = 'pending'
        this.PromiseResult = null
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
    }

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

    }
    reject(reason){
        if(this.PromiseState !== 'pending') return
        this.PromiseState = 'rejected'
        this.PromiseResult = reason
        while(this.onFulfilledCallbacks.length){
            this.onRejectedCallbacks.shift()(this.PromiseResult)
        }
    }
    then(onFulfilled,onRejected){
        // 参数校验,确保一定是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        

        const thenPromise = new MyPromise((resolve,reject) => {
            const resolvePromise = (cb) => {
                // setTimeout(() => {
                try {
                    console.log(this)
                    let x = cb(this.PromiseResult)
                    if(x instanceof MyPromise){
                        // x.then(value => resolve(value),err => reject(err))
                        x.then(resolve, reject)
                    }else{
                        resolve(x)
                    }
                } catch (error) {
                    reject(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
    }
}

const fn = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    },1000)
})
fn.then(res => {
    return new MyPromise((resolve,reject) => {
        resolve(res)
    })
},err => {
    console.log(err)
}).then(res1 => {
    console.log(res1)
})