一步步带你手写Promise

47 阅读5分钟

promise简写版

  1. Promise是个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行
  2. Promise有三个状态,分别为成功fullfilled、失败rejected,等待pending,一旦状态变化就不可更改
  3. resolve和reject函数是用来更改状态的
  4. then方法内做的事情就是判断状态,如果状态是成功,调用成功的函数,如果是失败,调用失败的函数
  5. then成功回调有一个参数,表示成功之后的值,失败的时候传失败的原因
  • 测试用例
new Promise((resolve,reject)=>{
    resolve('成功')
    // reject('失败')
})
  • 代码实现
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        this.status = MyPromise.PENDING
        this.value = undefined
        this.reason = undefined
        executor(this.resolve,this.reject)
    }
    resolve = (value)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.FULLFILLED
        this.value = value

    }
    reject = (reason)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.REJECTED
        this.reason = reason
    }
    then(successCallback,failCallback) {
        if(this.status===MyPromise.FULLFILLED){
            successCallback(this.value)
        }else if(this.status===MyPromise.REJECTED){
            failCallback(this.reason)
        }
    }
}

第二版,支持异步和then方法多次调用添加多个处理函数

  • 测试函数
let promise = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('成功')
    },2000)
    // reject('失败')
})
promise.then(value=>{
    console.log(value)
},reason=>{
    console.log(1)
    console.log(reason)
})
promise.then(value=>{
    console.log(2)
    console.log(value)
},reason=>{
    console.log(reason)
})
  • 代码实现
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        this.status = MyPromise.PENDING
        this.value = undefined
        this.reason = undefined
        this.successCallback = []
        this.failCallback = []
        executor(this.resolve,this.reject)
    }
    resolve = (value)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.FULLFILLED
        this.value = value
        while(this.successCallback.length){
            let cb = this.successCallback.shift()
            cb(this.value)
        }
    }
    reject = (reason)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.REJECTED
        this.reason = reason
        while(this.failCallback.length){
            let cb = this.failCallback.shift()
            cb(this.reason)
        }
    }
    then(successCallback,failCallback) {
        if(this.status===MyPromise.FULLFILLED){
            successCallback(this.value)
        }else if(this.status===MyPromise.REJECTED){
            failCallback(this.reason)
        }else {
            successCallback && this.successCallbacks.push(successCallback)
            failCallback && this.failCallback.push(failCallback)
        }
    }
}

第三版 实现then方法的链式调用

  1. 实现then方法的链式调用,需要返回一个promise
  2. 实现then方法值的传递:把上一个回调函数的return返回值,通过resolve传给下一个then方法
  3. return的值可以是普通值,也可以是一个promise对象
  • 测试用例
let promise = new Promise((resolve,reject)=>{
    resolve('成功')
    // reject('失败')
})
promise.then(value=>{
    console.log(value)
    // then方法return的值是下一个then方法接收到的value参数
    return 100
}).then(value=>{
    console.log(value)
})
// 支持.then return的值是promise的例子
function other(){
    return new Promise((resolve,reject)=>{
        resolve('other')
    })
}
promise.then(value=>{
    console.log(value)
    // then方法return的值是下一个then方法接收到的value参数
    return other()
}).then(value=>{
    console.log(value)
})
  • 代码实现
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        this.status = MyPromise.PENDING
        this.value = undefined
        this.reason = undefined
        this.successCallback = []
        this.failCallback = []
        executor(this.resolve,this.reject)
    }
    resolve = (value)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.FULLFILLED
        this.value = value
        while(this.successCallback.length){
            let cb = this.successCallback.shift()
            cb(this.value)
        }
    }
    reject = (reason)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.REJECTED
        this.reason = reason
        while(this.failCallback.length){
            let cb = this.failCallback.shift()
            cb(this.reason)
        }
    }
    then(successCallback,failCallback) {
        let promise2 = new MyPromise((resolve,reject)=>{
            if(this.status===MyPromise.FULLFILLED){
                let x = successCallback(this.value)
                // x是then成功回调函数的返回值,然后传给下一个then函数调用
                // 判断x的值是普通值还是promise对象
                // 如果是普通值,直接调用resolve
                // 如果是promise对象,查看promise对象返回的结果
                // 再根据promise对象返回的结果,决定调用resolve还是reject
                // 由于这段判断成功失败还是pending都需要调用,则把它封装成函数
                resolvePromise(x,resolve,reject)
            }else if(this.status===MyPromise.REJECTED){
                failCallback(this.reason)
            }else {
                successCallback && this.successCallbacks.push(successCallback)
                failCallback && this.failCallback.push(failCallback)
            }
        })
        return promise2
    }
}
function resolvePromise(x, resolve, reject){
    if(x instanceof MyPromise){
        // promise函数
        // 获取promise结果
        x.then(resolve,reject)
    }else {
        // 普通值
        resolve(x)
    }
}

第四版 实现then方法链式调用识别Promise对象自返回

注意事项: 1. 在then方法的回调函数中,不能return当前promise.then方法的返回的promise对象,要不会产生循环调用

  • 测试用例
let promise = new Promise((resolve,reject)=>{
    resolve(100)
    // reject('失败')
})
// !!!循环调用的例子
let p1 = promise.then(function(value){
    console.log(value)
    return p1
})
// 上边的报错要在下边的catch方法中捕获到
p1.then(()=>{

},err=>{
    console.log(err) //这里打印报错
})
  • 代码实现
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        this.status = MyPromise.PENDING
        this.value = undefined
        this.reason = undefined
        this.successCallback = []
        this.failCallback = []
        executor(this.resolve,this.reject)
    }
    resolve = (value)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.FULLFILLED
        this.value = value
        while(this.successCallback.length){
            let cb = this.successCallback.shift()
            cb(this.value)
        }
    }
    reject = (reason)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.REJECTED
        this.reason = reason
        while(this.failCallback.length){
            let cb = this.failCallback.shift()
            cb(this.reason)
        }
    }
    then(successCallback,failCallback) {
        let promise2 = new MyPromise((resolve,reject)=>{
            if(this.status===MyPromise.FULLFILLED){
                setTimeout(()=>{
                    // 这里如果x===promise2就代表返回了自己,需要调用reject抛出错误
                    let x = successCallback(this.value)
                    // 这里增加一个参数promise2
                    // 这里想要获取到promise2,需要变成异步代码才能获取到
                    resolvePromise(promise2,x,resolve,reject)
                },0)
               
            }else if(this.status===MyPromise.REJECTED){
                failCallback(this.reason)
            }else {
                successCallback && this.successCallbacks.push(successCallback)
                failCallback && this.failCallback.push(failCallback)
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject){
    if(promise2 === x){
       return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof MyPromise){
        // promise函数
        // 获取promise结果
        x.then(resolve,reject)
    }else {
        // 普通值
        resolve(x)
    }
}

第五版 捕获错误及链式调用其它代码补充

  1. 捕获错误及链式调用其它代码补充
  2. 捕获执行器和then回调函数里的报错
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        try {
            this.status = MyPromise.PENDING
            this.value = undefined
            this.reason = undefined
            this.successCallback = []
            this.failCallback = []
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    resolve = (value)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.FULLFILLED
        this.value = value
        while(this.successCallback.length){
            // 下边then方法里this.successCallbacks.push函数的时候传了值,这里不用传了
            this.successCallback.shift()()
        }
    }
    reject = (reason)=>{
        if(this.status!==MyPromise.PENDING) return
        this.status!==MyPromise.REJECTED
        this.reason = reason
        while(this.failCallback.length){
            this.failCallback.shift()()
        }
    }
    then(successCallback,failCallback) {
        let promise2 = new MyPromise((resolve,reject)=>{
            if(this.status===MyPromise.FULLFILLED){
                setTimeout(()=>{
                    try{
                        let x = successCallback(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
               
            }else if(this.status===MyPromise.REJECTED){
                setTimeout(()=>{
                    try{
                        let x = failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }else {
                successCallback && this.successCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = successCallback(this.value)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
                failCallback && this.failCallback.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = failCallback(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
            }
        })
        return promise2
    }
}

第六版 将then方法的参数变成可选参数

  • 测试用例
let promise = new Promise((resolve,reject)=>{
    resolve(100)
})
promise
    .then()
    .then()
    .then(value=>console.log(value))
// then方法什么都不传,等同于.then(value=>value),所以我们需要做的就是补充一下这个回调函数
promise
    .then(value=>value)
    .then(value=>value)
    .then(value=>console.log(value))
  • 代码实现
class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        try {
            this.status = MyPromise.PENDING
            this.value = undefined
            this.reason = undefined
            this.successCallback = []
            this.failCallback = []
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    // ......
    then(successCallback,failCallback) {
        successCallback = successCallback?successCallback:value=>value
        failCallback = failCallback?failCallback:reason=>{throw reason}
        // ...
    }
}

第七版 finally和catch方法

 class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        try {
            this.status = MyPromise.PENDING
            this.value = undefined
            this.reason = undefined
            this.successCallback = []
            this.failCallback = []
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    // ......
    finally(cb){
        return this.then(value=>{
            // 兼容cb()是异步的轻举昂
            return MyPromise.resolve(value(cb())).then(()=>value)
        },reason=>{
            return MyPromise.resolve(value(cb())).then(()=>{throw reason})
        })
    }
    catch(failCallback){
        return this.then(undefined,failCallback)
    }
}

扩展

promise.all方法的实现,解决异步并发

  1. promise.all方法的实现,解决异步并发
  2. 接收的参数是一个数组,包括普通值和promise,
  3. 返回的值也是promise对象,
  4. 数组需要按照传入的顺序返回
  5. 静态方法
  • 测试用例
function p1(){
    return new Promise((resolve,reject)=>{
        setTimeout(function(){
            resolve('p1')
        },2000)
    })
}
function p2(){
    return new Promise((resolve,reject)=>{
        resolve('p2')
    })
}
Promise.all(['a','b',p1(),p2(),'c']).then(function(result){
    // result=>['a','b','p1','p2','c']
})

代码实现

class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        try {
            this.status = MyPromise.PENDING
            this.value = undefined
            this.reason = undefined
            this.successCallback = []
            this.failCallback = []
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    // ......
    static all(arr){
        let result = []
        let index = 0
        return new MyPromise((resolve,reject)=>{
            function addData(key,value){
                result[key] = value
                index++
                if(index === arr.length){
                    resolve(result)
                }
            }
            for(let i = 0; i<arr.legnth; i++){
                if(arr[i] instanceof MyPromise){
                    arr[i].then(value=>addData(i,value),reason=>reject(reason))
                }else{
                    addData(i,arr[i])
                }
            }
        })
    }
}

Promise.resolve方法的实现,将resolve的值转为promise

 class MyPromise{
    static PENDING = 'pending'
    static FULLFILLED = 'fullfilled'
    static REJECTED = 'rejected'
    constructor(executor){
        try {
            this.status = MyPromise.PENDING
            this.value = undefined
            this.reason = undefined
            this.successCallback = []
            this.failCallback = []
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    // ......
    static resolve(value){
        if(value instanceof MyPromise) return
        return new MyPromise(resolve=>resolve(value))
    }
}