zoukankan      html  css  js  c++  java
  • 异步编程 -- 手写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
    

    写的不好,仅供娱乐

  • 相关阅读:
    九九乘法表
    数据汇总特殊处理-标题都在第N行
    Python库——Faker 安装及用法
    faker库 生成数据导入文件
    faker库的使用 faker是一个第三方Python包,为您生成你所需要的任何(假)数据。 安装:pip install faker
    生成随机数据:faker库
    运算符
    初识编码
    网页设计基础(二)
    网页设计基础
  • 原文地址:https://www.cnblogs.com/MissSage/p/14890772.html
Copyright © 2011-2022 走看看