码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2021-06-17 16:31:23      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:cal   ack   理解   let   cut   完成后   completed   catch   思考   

Promise模拟,尝试手写Promise

思考

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

还可以通过Promise.resolve()和Promise.all()使用,这两方法应该是静态的,所以不通过实例调用,只能是只能类直接调用,所以大概的样子可能是像下面这样:

class MyPromise{
  constructor(executor){
    executor(this.resolve,this.reject)
  }
  resolve(){}
  reject(){}
  static resolve()
  static all()
}
  • 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实例

  • 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执行失败回调
  • reject()

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

  1. 判断状态,不是PENDING将直接返回
  2. 更改状态
  3. 赋值,reject传入的值即发生错误的原因
  4. 执行错误回调
  • then()

then方法接收两个参数,一个successCallback,一个failCallback,参数可选,因为then方法可以链式调用,且方法可能有异步函数,所有采用数组存储的方法来存储回调,then方法返回的是Promise对象

  • Promise.resolve()

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

  • Promise.all()

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

  • finally()

finally方法其实就是一个then方法,只是它只接收一个回调

代码编写

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

// 定义三个状态常量
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

写的不好,仅供娱乐

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

标签:cal   ack   理解   let   cut   完成后   completed   catch   思考   

原文地址:https://www.cnblogs.com/MissSage/p/14890772.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!