实现一个promise(1)

·  阅读 183

今天我们来自己实现一个promise,首先看下promise的用法

function getData(){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        let num = Math.random()
        if (num > 0.5) {
          resolve('成功了')
        } else {
          reject('失败了')
        }
      },1000)
    })
}
let p1 = getData()
p1.then(data=>{
    console.log('p1成功了吗:'+data)
},err=> {
    console.log('p1失败了吗:'+err)
})
复制代码

简易版

根据上面的用法,我们先来实现一个简易版的功能:

  1. promise 接收一个参数fn,这个参数是个函数
  2. 参数fn接收两个参数: resolve和reject, 这两个参数也分别是函数
  3. promise 上有一个then方法,这个方法可接收两个参数,这两个参数也是函数,其实我们可以看做这两个函数是用户传进来的 成功与失败时分别的回调
  4. promise 意思就是承诺,承诺给你成功了就调用你的成功函数,失败了就调用你的失败函数,那么我们应该给promise一个状态机,这个状态会有一种结果,要么就是成功要么就是失败。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(fn) {
    this.status = PENDING; //初始状态
    this.value = null;//成功时的值
    this.reason = null;//失败时的原因
    this.resolveCallBack = null; //用户在then方法中传入的成功回调
    this.rejectCallBack = null; //用户在then方法中传入的失败回调
    
    //promise所属的两个函数,为什么要用setTimeout?因为要确保在调用reject 或者 resolve的时候,then 中传入的回调函数已经被注册进来了
    this.resolve = (data) =>{
        setTimeout(()=>{
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.resolveCallBack(data)
            } 
        },0)
    }
    this.reject = (err) =>{
        setTimeout(()=>{
            if (this.status === PENDING) {
                this.status = REJECTED
                this.rejectCallBack(err)
            }    
        },0)
    }
    fn.call(undefined,this.resolve,this.reject) //最后 执行一下传入的fn函数
}
// 在then 方法中将用户传入的函数 分别放存放在我们变量中,当异步代码执行完毕之后,
//用户调用我们的resolve或reject方法时,我们就在resolve 或reject里面去调用对应的这两个函数中的一个,并将用户传过来的值再回传给这两个函数中的其中一个,这样在then 中 就可以接收到了
MyPromise.prototype.then = function(resolveCallBack,rejectCallBack){
    this.resolveCallBack = resolveCallBack;
    this.rejectCallBack = rejectCallBack;
}
复制代码

现在来使用一下我们的promise

function getData(){
    return new MyPromise((resolve,reject)=>{
        setTimeout(()=>{
            if (Math.random() > 0.5) {
                resolve('成功了')
            } else {
                reject('失败了')
            }
        },1000)
    })  
}
let p1 = getData()
p1.then(
    data=>{
        console.log('p1成功了吗?'+data)
    },
    err=>{
        console.log('p1失败了吗?'+err)
    }
)
在控制台可以看到随机打印出 'p1成功了吗 成功了'或者'p1失败了吗 失败了'
复制代码

实现同一个promise的多次调用

实现了简易版的promise之后,我们就可以慢慢地完善它了,首先就是 同一个promie我们可以多次调用,就像这样

p1.then(data=>{ 
    console.log('第一次p1成功了吗?'+data)
},err=>{
   console.log('第一次p1失败了吗?'+err) 
})
p1.then(data=>{ 
    console.log('第二次p1成功了吗?'+data)
},err=>{
   console.log('第二次p1失败了吗?'+err) 
})
p1.then(data=>{ 
    console.log('第三次p1成功了吗?'+data)
},err=>{
   console.log('第三次p1失败了吗?'+err) 
})
复制代码

那我们看到这种用法就是 同一个promise对象上注册了很多个回调了,那只要我们在添加回调的地方用一个数组来存储这些回调,然后在执行回调的地方遍历执行就ok了。 下面开始修改我们的代码了(修改的地方我都有标记出来了):

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(fn) {
    this.status = PENDING; 
    this.value = null;
    this.reason = null;
    // this.resolveCallBack = null; 
    // this.rejectCallBack = null; 
    
    //这里改成数组来存储回调
    this.resolveCallBackArray = []; 
    this.rejectCallBackArray = [];
    
    this.resolve = (data) =>{
        setTimeout(()=>{
            if (this.status === PENDING) {
                this.status = FULFILLED
                // this.resolveCallBack(data)
                //这里改成遍历回调数组,依次执行回调
                this.resolveCallBackArray.forEach(fn =>{
                    fn(data)
                })
            } 
        },0)
    }
    this.reject = (err) =>{
        setTimeout(()=>{
            if (this.status === PENDING) {
                this.status = REJECTED
                // this.rejectCallBack(err)
                //这里改成遍历回调数组,依次执行回调
                this.rejectCallBackArray.forEach(fn =>{
                    fn(err)
                })
            }    
        },0)
    }
    fn.call(undefined,this.resolve,this.reject) //最后 执行一下传入的fn函数
}
MyPromise.prototype.then = function(resolveCallBack,rejectCallBack){
    // this.resolveCallBack = resolveCallBack;
    // this.rejectCallBack = rejectCallBack;
    //这里改成将回调依次push进入数组
    this.resolveCallBackArray.push(resolveCallBack)
    this.rejectCallBackArray.push(rejectCallBack)
}

再执行我们上面的p1
p1.then(data=>{ 
    console.log('第一次p1成功了吗?'+data)
},err=>{
   console.log('第一次p1失败了吗?'+err) 
})
p1.then(data=>{ 
    console.log('第二次p1成功了吗?'+data)
},err=>{
   console.log('第二次p1失败了吗?'+err) 
})
复制代码

可以看到 会依次打印出 '第一次p1成功了吗?成功了' ,'第二次p1成功了吗?成功了' 可以自己试试看

到目前为止,我们实现了一个非常简易的prmomise,有任何问题,希望大家能给我指出,接受任何批评!下一篇文章,再来继续完善我们的promise

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改