手写简易promise!实现promise链式调用

今天带大家实现手写promise啊!加油加油


简介

promise作为处理异步的常见方法,它可以使异步代码看起来像同步一样执行。它用来解决地狱回调问题,使得代码可读性大大增加。

一个 Promise 必然处于以下几种状态之一:

待定(pending):初始状态,既没有被兑现,也没有被拒绝。

已兑现(fulfilled):意味着操作成功完成。

已拒绝(rejected):意味着操作失败。

难点

对于实现手写promise主要有这么几个难点

  1. 代码的执行顺序问题
  2. 代码的this指向问题
  3. 链式调用会出现的递归问题

实现代码

简单promise

首先我们凭直觉应该能写出这样的代码,即promise是一个class,在new promise的过程中其实是在创建这个class的实例。

而这个class中至少应该有这么几个方法:

  1. constructor :用于接收这个回调函数并执行
  2. resolve :用于处理状态转变为fulfilled后所需要做的事情
  3. reject :用于处理状态转变为rejected后所需要做的事情
  4. then :用于接收状态改变为fulfilled/rejected后的回调函数

需要注意的点在于这段代码,这么写是因为constructor内执行的是个异步函数,它的执行顺序并不确定,如果他是setTimeout(()=>{},0)那么它就比then快,如果不是0秒他就会比then慢,所以这里要分情况讨论

kotlin 复制代码
 then(f1:IResolve,f2?:IReject){
        if(this.status=="fulfilled"){
            console.log("运行了成功")
            f1(this.data)

        }else if(this.status=="rejected"){
            f2(this.data)
        }else if(this.status=="padding"){
            this.f1f2.push([f1,f2])
        }
    }

然后还有一些小的this指向问题,this丢了再用call/bind绑定一下就行了!

然后我们凭借程序员的直觉就能写出下面的代码了!。(❁´◡`❁)

kotlin 复制代码
type IFn=(resolve:IResolve,reject:IReject)=>void
type IResolve<T=any>=(data:T)=>void
type IReject<T=any>=(data:T)=>void
class Promise2{
    private status='pending'
    private f1f2:[IResolve,IReject][]=[]
    private data:any
    constructor(fn:IFn) {
        fn.call(this,this.resolve.bind(this),this.reject.bind(this));
    }
    resolve(data):IResolve{
        console.log("运行resolve")
        this.status="fulfilled"
        this.data=data;
        let arriveF1f2=this.f1f2.shift();
        if(arriveF1f2){
            arriveF1f2[0](data);
        }
        return

    }

    reject(data):IReject{
        this.status="rejected"
        this.data=data;
        let arriveF1f2=this.f1f2.shift();
        if(arriveF1f2[1]){
            arriveF1f2[1](data);
        }
        return
    }

    then(f1:IResolve,f2?:IReject){
        if(this.status=="fulfilled"){
            console.log("运行了成功")
            f1(this.data)

        }else if(this.status=="rejected"){
            f2(this.data)
        }else if(this.status=="padding"){
            this.f1f2.push([f1,f2])
        }
    }
}
let test=new Promise2((resolve, reject)=>{
    setTimeout(()=>{
        reject("成功");
    })
})
test.then((data)=>{
    console.log("运行了回调1")
    console.log(data);
},(data)=>{
    console.log("运行了失败回调1");
})

链式调用promise

众所周知promise是可以实现链式调用的,即在.then之后继续.then。

那我们如果要实现这个功能其实也很简单,就是通过then函数返回一个新的promise来实现,然后通过执行resolve/reject函数来实现运行成功回调还是失败回调,具体是下面这几行代码

kotlin 复制代码
    then(f1:IResolve,f2?:IReject){
            return new Promise2((resolve, reject)=>{
                console.log("运行了then",this.status);
                if(this.status=="fulfilled"){
                    console.log("运行了成功")
                    this.parse(f1(this.data),resolve,reject)

                }else if(this.status=="rejected"){
                    this.parse(f2(this.data),resolve,reject)


                }else if(this.status=="pending"){
                    this.f1f2.push({
                        f1:()=>{this.parse(f1(this.data),resolve,reject)},
                        f2:()=>(this.parse(f2(this.data),resolve,reject))
                    })
                }
            })//为了实现链式调用,所以返回promise
    }

    //冗余代码
    parse(result,resolve,reject){
        try {
            if(result instanceof Promise2){
                result.then(resolve,reject)
            }else{
                resolve(result);
            }
        }catch (e){
            reject(result)
        }
    }

完整代码如下

kotlin 复制代码
type IFn=(resolve:IResolve,reject:IReject)=>void
type IResolve<T=any>=(data:T)=>any
type IReject<T=any>=(data:T)=>any
class Promise2{
    private status='pending'
    private f1f2:any[]=[]
    private data:any
    constructor(fn:IFn) {
        fn.call(this,this.resolve.bind(this),this.reject.bind(this));
    }
    resolve(data):IResolve{
        // console.log(this);
        console.log("运行resolve")
        this.status="fulfilled"
        this.data=data;
        let arriveF1f2=this.f1f2.shift();
        if(arriveF1f2?.f1){
            arriveF1f2.f1();
        }
        return

    }

    reject(data):IReject{
        this.status="rejected"
        this.data=data;
        let arriveF1f2=this.f1f2.shift();
        if(arriveF1f2.f2){
            arriveF1f2.f2();
        }
        return
    }

    then(f1:IResolve,f2?:IReject){
            return new Promise2((resolve, reject)=>{
                console.log("运行了then",this.status);
                if(this.status=="fulfilled"){
                    console.log("运行了成功")
                    this.parse(f1(this.data),resolve,reject)

                }else if(this.status=="rejected"){
                    this.parse(f2(this.data),resolve,reject)


                }else if(this.status=="pending"){
                    this.f1f2.push({
                        f1:()=>{this.parse(f1(this.data),resolve,reject)},
                        f2:()=>(this.parse(f2(this.data),resolve,reject))
                    })
                }
            })//为了实现链式调用,所以返回promise
    }

    //冗余代码
    parse(result,resolve,reject){
        try {
            if(result instanceof Promise2){
                result.then(resolve,reject)
            }else{
                resolve(result);
            }
        }catch (e){
            reject(result)
        }
    }
}
let test=new Promise2((resolve, reject)=>{
    setTimeout(()=>{
        resolve("成功");
    })
})
test.then((data)=>{
    console.log("运行了回调1")
    console.log(data);
},(data)=>{
    console.log("运行了失败回调1");
}).then((data)=>{
    console.log("运行了回调2")
    console.log(data);
},(data)=>{
    console.log("运行了失败回调2");
})

这大概就是实现promise的完整过程啦!写完这个过程还是很考验js基础的我感觉!

好啦我是发面糕!我们下回再见!(●'◡'●)

相关推荐
Jiaberrr6 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho1 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记2 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java3 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele3 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀3 小时前
CSS——属性值计算
前端·css
睡觉然后上课3 小时前
c基础面试题
c语言·开发语言·c++·面试
DOKE4 小时前
VSCode终端:提升命令行使用体验
前端