异步编程 -- 手写Promise初体验

Promise模拟,尝试手写Promise

思考

Promise通过new Promise((resolve,reject)=>{}) 使用,并通常会传入一个函数,这个函数有两个参数,一个是resolve,一个是reject,resolve应该是一个可以解决事情的函数,reject应该是一个当事情解决失败时的处理函数,所以Promise应该是一个类,此类的构造器应该传入一个执行器,用来执行处理函数,并且应该有两个处理函数resolve和reject

  • Promise有哪些属性和方法?

    从平常使用中可以知道,Promise有如下几个方法供我们使用:

    • resolve(value),用来处理事情,此方法接收一个参数,可以在Promise实例中直接使用,所有应该考虑其this指向问题
    • reject(reason),用来处理不能处理事情时的情况,此方法接收一个参数,与resolve相同,可以在Promise实例中直接使用,应该考虑其this指向
    • then(successCallback,failCallback),这个用来处理当实例中的操作完成后进行的后续操作,可以多次链式调用,此方法接收两个参数,一个是成功的回调,一个是失败的回调,参数都是可选的,每次返回的都是一个Promise实例
    • finally(callbacl),当Promise的事情处理完后,会进入到此方法,此方法接收一个回调,此方法也是返回一个Promise实例,因此,理论上finally后面还是可以继续链式调用then方法的
    • catch(callback),当Promise实例执行发生错误时,会进入到此方法,此方法接收一个回调参数来处理错误
    • Promise.resolve(value),这是一个静态方法,只能直接通过Promise类使用,不能通过实例调用,接收一个参数,返回Promise实例
    • Promise.all(array),这也是一个静态方法,只能直接通过Promise类使用,不能通过实例调用,接收一个数组参数,返回Promise实例
    class MyPromiseRepeat{
      constructor(executor){
        executor(this.resolve,this.reject)
      }
      resolve(value){}
      reject(reason){}
      then(successCallback,failCallback){}
      static resolve(value)
      static all(array)
      finally(callback){}
      catch(callback){}
    }
    
  • Promise怎么进行过程控制的?

    通过查看Promise原码,发现其使用了几个状态值,pending表示事情未完成成,fulfilled表示事情完成,rejected表示事情失败,那么可以定义这几个常量先:

const PENDING='pending'
const FULFILLED='fulfilled'
const REJECTED='rejected'
  • 那这些状态应该是怎么改变的呢?

    首先,状态默认是PENDING状态,当事情处理完成后,状态会变成FULFILLED,当事情失败时,状态会变成REJECTED,那么肯定是有一个变量来储存这个状态了,就叫在Promise中再加一个Status吧

Status=PENDING
后续的resolve和reject就可以改变状态,请继续看
  • resolve()

    想一下resolve方法都是干嘛的,resolve的作用有几个:

    1. 判断状态,resolve方法,你可以调用多次,但我不会执行多次,只会执行第一次,因为在这里,要判断判断是否是PENDING,如果不是,那么直接返回,因为不是PENDING表示已经处理过事情了,不需要再处理了,这样讲可能有点牵强,但是多理解理解应该会懂
    2. 更改状态,经过resolve后,Promise的状态就会改变,在resolve中会改变成FULFILLED
    3. 赋值,怎么说?当然是把传入的参数经过处理后存在起来呀,存哪里,那就在Promise中再添加一个value吧
    4. 执行回调,这个又怎么说?因为在then方法中会传入回调,那这个回调是直接调用的吗?不一定,当同步执行时,可以直接调用,但是是异步时,就不行了,所有需要把传入then的回调存储起来,那这些回调在什么时候执行?有两个地方,一个就是在resolve中,当然,这里指的回调是成功回调,另一个就是reject了,reject执行失败回调
// 定义全局变量value,用来存储处理方法resolve的参数,默认为undefined
value=undefined
resolve=(value)=>{
    // 判断状态
    if(this.Status!=PENDING) return
    // 更改状态
    this.Status=FULFILLED
    // 赋值
    this.value=value
    // 执行成功回调
    while(this.successCallback.length) this.successCallback.shift()()
}
  • reject()

    与resolve一样,需要做以下几件事:

    1. 判断状态,不是PENDING将直接返回
    2. 更改状态
    3. 赋值,reject传入的值即发生错误的原因
    4. 执行错误回调
// 定义全局变量reason,用来存储错误的原因,默认值为undefined
reason=undefined
reject=(reason)=>{
    if(this.Status!=PENDING)return
    this.reason=reason
    this.Status=REJECTED
    while(this.failCallback.length) this.failCallback.shift()()
}
  • then()

    then方法接收两个参数,一个successCallback,一个failCallback,参数可选,, 在then方法需要对参数进行判断,首先是没有传入参数时的处理情况,默认是上一次处理的结果,上一处理失败则会是undefined,因为then方法可以链式调用,且方法可能有异步函数,当传入参数为异步操作时,需要将传入的异步回调用数组存储起来,then方法返回的是Promise对象

// 定义两个数组来存储异步回调
successCallback=[]
failCallback=[]
then(successCallback,failCallback){
    // 让参数可选,
    successCallback=successCallback?successCallback:value=>value
    failCallback=failCallback?failCallback:reason=>{throw reason}
    // 返回Promise实例
    
    let promise2= new MyPromiseRepeat((resolve,reject)=>{
        // 成功时有执行
        if(this.Status==FULFILLED){
            // 为了能够调用此Promise实例,采用setTimeout来保证拿到promise2
            setTimeout(()=>{
                // tryCatch截取错误并交给reject
                try {
                    let x=successCallback(this.value)
                    resolvePromise(promise2,x,resolve,reject)

                } catch (error) {
                    this.reject(error)
                }
            },0)
         // 失败时执行
        }else if(this.Status==REJECTED){
            setTimeout(()=>{
                try {
                    let x=failCallback(this.reason)
                    resolvePromise(promise2,x,resolve,reject)
                } catch (error) {
                    this.reject(error)
                }
            },0)
        }else{// 异步操作还没有完成时执行
            this.successCallback.push(()=>{
                setTimeout(()=>{
                    try {
                        let x=successCallback(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    } catch (error) {
                        this.reject(error)
                    }
                },0)
            })
            this.failCallback.push(()=>{
                setTimeout(()=>{
                    try {
                        let x=failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    } catch (error) {
                        this.reject(error)
                    }
                },0)
            })
        }
    })
    return promise2
}
// resolvePromise方法
function resolvePromise(promise2,x,resolve,reject){
    // 这里是识别自调用
    if(promise2===x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 当异步操作则then
    if(x instanceof MyPromiseRepeat){
        x.then(resolve,reject)
    }else{
        resolve(x)
    }
}
  • MyPromiseRepeat.resolve()

    与resolve方法不同,Promise.resolve是把传入的参数转换成Promise对象并返回,所以这个很简单,当传入的参数不是Promise对象时,返回一个新的Promise实例,这个实例resolve了传入的参数,当传入的参数是Promise实例时,直接返回参数

static resolve(value){
    if(value instanceof MyPromiseRepeat) return value
    else{
        // 不是Promise实例则返回新Promise实例
        return new MyPromiseRepeat((resolve)=>{
            resolve(value)
        })
    }
}
  • Promise.all()

    当所有参数数组中的情事都进行完成并成功后,算成功,返回Promise实例,当有一件事情失败时,就算失败,返回Promise实例,这里需要注意的是怎么去判断数组中的事情是否都执行完成,可以用一个数组 result去存储事情结果,遍历参数数组,并用一个数count来记住添加到result中的结果,如果数组项是Promise实例,则通过此项的then方法去向result中添加一个事情,count++,如果不是Promise对象,则直接添加到result中,count++,这样就能保证异步事情都可以在处理完成后把结果存储到result中,当count==参数数组的长度时,就表示参数中的所有异步任务都执行完成并添加到了result中,这时就可以resolve(result)来返回结果了

static all(array){
    // 存储结果
    let result=[]
    // 记数count
    let resultCompletedCount=0
    // 返回新Promise实例
    return new MyPromiseRepeat((resolve,reject)=>{
        // 添加结果到result的方法
        function AddResult(index,value){
            result[index]=value
            resultCompletedCount++
            // 当result长度和参数array一样长时,resolve(result)
            if(resultCompletedCount==array.length){
                resolve(result)
            }
        }
        // 遍历参数数组,处理每一项,是Promise实例则调用其then方法来获取结果,不是Promise实例则直接添加到result中
        for(let i =0;i<array.length;i++){
            let current= array[i]
            if(current instanceof MyPromiseRepeat){
                // 为能保证全部执行,异步操作完成后再把结果添加到result
                current.then(value=>AddResult(i,value),reason=>reject(reason))
            }else{
                AddResult(i,array[i])
            }
        }
    })
}
  • finally()

    finally方法其实就是一个then方法,只是它只接收一个回调,方法内部调用then方法,根据callback的执行结果来决定返回结果

// ...,
finally(callback){
    return this.then(value=>{
        return MyPromiseRepeat.resolve(callback()).then(()=>value)
    },reason=>{
        return MyPromiseRepeat.resolve(callback()).then(()=>{throw reason})
    })
}
  • catch方法

    catch方法也和then方法一样,只接收第二个参数,即失败处理回调

catch(callback){
    return this.then(undefined,callback)
}

代码编写

通过上面的分析,写出如下代码:

// 定义三个状态常量
const PENDING='pending'
const FULFILLED='fulfilled'
const REJECTED='rejected'
//  创建Promise类
class MyPromiseRepeat{
    // 构造函数接收一个执行器
    constructor(executor){
        //用tryCatch截取错误,并交给reject处理
        try {
            executor(this.resolve,this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    // resolve参数
    value=undefined
    // 失败原因
    reason=undefined
    // 状态
    Status=PENDING
    // 成功回调存储
    successCallback=[]
    // 失败回调存储
    failCallback=[]
    // reslove方法
    resolve=(value)=>{
        // 判断状态
        if(this.Status!=PENDING) return
        // 更改状态
        this.Status=FULFILLED
        // 赋值
        this.value=value
        // 执行成功回调
        while(this.successCallback.length) this.successCallback.shift()()
    }
    reject=(reason)=>{
        if(this.Status!=PENDING)return
        this.reason=reason
        this.Status=REJECTED
        while(this.failCallback.length) this.failCallback.shift()()
    }
    then(successCallback,failCallback){
        // 让参数可选,
        successCallback=successCallback?successCallback:value=>value
        failCallback=failCallback?failCallback:reason=>{throw reason}
        // 返回Promise实例
        
        let promise2= new MyPromiseRepeat((resolve,reject)=>{
            // 成功时有执行
            if(this.Status==FULFILLED){
                // 为了能够调用此Promise实例,采用setTimeout来保证拿到promise2
                setTimeout(()=>{
                    // tryCatch截取错误并交给reject
                    try {
                        let x=successCallback(this.value)
                        resolvePromise(promise2,x,resolve,reject)
    
                    } catch (error) {
                        this.reject(error)
                    }
                },0)
             // 失败时执行
            }else if(this.Status==REJECTED){
                setTimeout(()=>{
                    try {
                        let x=failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    } catch (error) {
                        this.reject(error)
                    }
                },0)
            }else{// 异步操作还没有完成时执行
                this.successCallback.push(()=>{
                    setTimeout(()=>{
                        try {
                            let x=successCallback(this.value)
                            resolvePromise(promise2,x,resolve,reject)
                        } catch (error) {
                            this.reject(error)
                        }
                    },0)
                })
                this.failCallback.push(()=>{
                    setTimeout(()=>{
                        try {
                            let x=failCallback(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        } catch (error) {
                            this.reject(error)
                        }
                    },0)
                })
            }
        })
        return promise2
    }
    finally(callback){
        return this.then(value=>{
            return MyPromiseRepeat.resolve(callback()).then(()=>value)
        },reason=>{
            return MyPromiseRepeat.resolve(callback()).then(()=>{throw reason})
        })
    }
    catch(callback){
        return this.then(undefined,callback)
    }
    static resolve(value){
        if(value instanceof MyPromiseRepeat) return value
        else{
            // 不是Promise实例则返回新Promise实例
            return new MyPromiseRepeat((resolve)=>{
                resolve(value)
            })
        }
    }
    static all(array){
        let result=[]
        let resultCompletedCount=0
        return new MyPromiseRepeat((resolve,reject)=>{
            function AddResult(index,value){
                result[index]=value
                resultCompletedCount++
                // 当result长度和参数array一样长时,resolve(result)
                if(resultCompletedCount==array.length){
                    resolve(result)
                }
            }
            for(let i =0;i<array.length;i++){
                let current= array[i]
                if(current instanceof MyPromiseRepeat){
                    // 为能保证全部执行,异步操作完成后再把结果添加到result
                    current.then(value=>AddResult(i,value),reason=>reject(reason))
                }else{
                    AddResult(i,array[i])
                }
            }
        })
    }

}

function resolvePromise(promise2,x,resolve,reject){
    // 这里是识别自调用
    if(promise2===x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 当异步操作则then
    if(x instanceof MyPromiseRepeat){
        x.then(resolve,reject)
    }else{
        resolve(x)
    }
}
module.exports=MyPromiseRepeat

测试结果

// index.js
const MyPromise = require('./MyPromiseRepeat.js')
let p1=function(){
    return new MyPromise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('p1')
        },2000)
    })
}
let p2=function(){
    return new MyPromise((resolve,reject)=>{
        resolve('p2')
        // reject('失败')
    })
}
MyPromise.all(['a','b',p1(),p2(),'c']).then(res=>console.log(res),reason=>console.log(reason)) //此处对应输出['a','b','p1','p2','c']
// MyPromise.resolve('1').then((value)=>console.log(value))
// MyPromise.resolve(p1()).then((value)=>console.log(value))
p2().catch((reason)=>{
    console.log(reason)
}).finally(()=>{
    console.log('finally')
    // 这里返回p1()但是继续then的话,拿到的还是p2的结果"p2",因为在finally中返回的就是finally的调用者p2的实例,如果p2错误,则拿到的是undefined
    return p1()
}).then(value=>console.log(value),reason=>console.log(reason))//此处对应输出finally p2

// 最终输出:
// finally
// ['a','b','p1','p2','c']
// p2

// 如果p2 reject了,Promise.all则失败,所以输出'失败',后面catch也会捕获错误,输出'失败',finally不受影响,输出'finally',finally.then则是输出undefined,因为p2失败了,没有结果传递下来,则最终输出:
// 失败
// 失败
// finally
// undefined

写的不好,仅供娱乐

posted @ 2021-06-16 19:36  MissSage  阅读(86)  评论(0编辑  收藏  举报