手写一个简易的promise,通俗易懂版本

208 阅读5分钟

首先简单看一下promiseA+规范

(1)Promise对象代表一个异步操作,有三种状态 :pending(进行中)、fulfilled(已成功)和 rejected(已失败)。一旦成功就不允许失败,一旦失败就不允许成功

这里的代码可以这样写

    function MyPromise(){ //构造函数所以首字母大写
        const self = this 
        self.status = 'pending'
        self.value = null
        self.reason = null
    }

(2)Promise接收一个函数作为参数,该函数有两个参数,一个是resolve,表示成功时执 行的函数,一个是reject,表示失败失败时执行的函数。 resolve执行时传入的参数会作为then方法中第一个回调函数的参数,reject执行传入的参数会作为then方法中第二函数回调的参数。

我们来补充一下上面的myPromise构造函数

    function MyPromise(excutor){
        const self = this 
        self.status = 'pending'
        self.value = null
        self.reason = null
        function resolve(value){ // resolve函数接受成功时传的参数
          if(self.status==='pending'){
            self.status = 'fulfailled'  //改变当前执行函数的状态为成功状态
            self.value= value //存储成功时的值
          }
      }
     function reject(reason){ // reject函数接受失败时传的参数
      if(self.status==='pending'){
        self.status = 'rejected'  //改变当前执行函数的状态为失败的状态
        self.resaon = reason    // 存储失败时的值
      }
    }
    excutor(resolve,reject)
}
  //  此时在控制台输出myPromise实例 例如 
   const mp1 =  new MyPromise((resolve,reject)=>{
        resolve('test');
     // reject('testerror')
    })
    console.log(mp1)
    // 会输出  
    MyPromise{
        reason: null
        status: "fulfailled"
        value: "test"
    } 
    or
    MyPromise{
        reason: 'teserror'
        status: "rejected"
        value: null
    } 

是不是有点头绪了

把MyPromsie对象里面的 vaule和reason传到then的第一个函数和第二个函数的参数里面就可以了
那我们来实现一下then

//挂载在原型是为了每一个new MyPromise()都可以使用
MyPromise.prototype.then = function(onFulfailled, onRejected){
    const self = this
    if(self.status==='fulfailled'){
    onFulfailled(self.value)
    }
    if(self.status==='rejected'){
    onRejected(self.reason)
    }
} 
// ok此时then里面就可以接受resolve传的value和reject传的reason
new MyPromise((resove,reject)=>{
    resolve('test1')
}).then(res=> {
    console.log(res)
}) //此时控制台会打出test1

好了到此一个同步版本的promise就实现的差不多了 我们来看看还剩下的问题

1,异步处理

比如还是上面的例子

//使用setimeout模拟异步
new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('test1')
    },1000)
}).then(res=>{
    console.log(res)
}) //此时控制台无输出

那我们先来解决这个问题,首先这里then里面没有获取到setimeout里面的resolve的值, 是因为then在setTimeout之前就执行了,现在只要让then可以确保一定在setTimeout执行之后可以拿到resolve里面传的值就可以了。

那怎么解决这个问题呢? 我们是不是先把then的方法存储起来,然后再promise实例执行完的时候去再去执行then里面存储的方法并且把参数传进去

我们先来用两个数组存储成功和失败的方法,改造一下MyPromise构造函数

function MyPromise(excutor){
        const self = this 
        self.status = 'pending'
        self.value = null
        self.reason = null
        self.onFulfailledCallbacks = [] //存储成功的方法
        self.onRejectedCallbacks = [] //存储失败的方法
        function resolve(value){ // resolve函数接受成功时传的参数
          if(self.status==='pending'){
            self.status = 'fulfailled'  //改变当前执行函数的状态为成功状态
            self.value= value //存储成功时的值
            self.onFulfailledCallbacks.forEach(item=>item(self.value))
            // promise实例状态成功,执行之前存储成功方法的数组,把传给resolve的参数传进去
          }
      }
     function reject(reason){ // reject函数接受失败时传的参数
      if(self.status==='pending'){
        self.status = 'rejected'  //改变当前执行函数的状态为失败的状态
        self.resaon = reason    // 存储失败时的值
        self.onRejectedCallbacks.forEach(item=>item(self.reason))
        //promise实例状态失败,执行之前存储成功方法的数组把传给reject的参数传进去
      }
    }
    excutor(resolve,reject)
}

我们再来改造一下then方法

MyPromise.prototype.then = function(onFulfailled, onRejected){
   const self = this
    if(self.status==='fulfailled'){
     onFulfailled(self.value)
    }
    if(self.status==='rejected'){
     onRejected(self.reason)
    }
    if(self.staus==='pending'){ 
    //处于pending状态也就是promsie实例还在执行的时候,异步状态情况下
      self.onFulfailledCallbacks.push(onFulfailed)
      self.onRejectedCallbacks.push(onRejected)
    }
} 

自此myPromise就支持异步了

//改造后,使用setimeout模拟异步
new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('test1')
    },1000)
}).then(res=>{
    console.log(res)
}) //此时控制台输出test1

2,异常怎么处理,我们的promise有reject去处理异常,我们肯定希望我们所有是的异常都是通过这个函数处理,而不是系统报错

我们再改造一下MyPromise用try,catch来捕获异常,发现异常交给reject处理

function MyPromise(excutor){
        const self = this 
        self.status = 'pending'
        self.value = null
        self.reason = null
        self.onFulfailledCallbacks = [] //存储成功的方法
        self.onRejectedCallbacks = [] //存储失败的方法
        function resolve(value){ // resolve函数接受成功时传的参数
          if(self.status==='pending'){
            self.status = 'fulfailled'  //改变当前执行函数的状态为成功状态
            self.value= value //存储成功时的值
            self.onFulfailledCallbacks.forEach(item=>item(self.value))
            // promise实例状态成功,执行之前存储成功方法的数组,把传给resolve的参数传进去
          }
      }
     function reject(reason){ // reject函数接受失败时传的参数
      if(self.status==='pending'){
        self.status = 'rejected'  //改变当前执行函数的状态为失败的状态
        self.resaon = reason    // 存储失败时的值
        self.onRejectedCallbacks.forEach(item=>item(self.reason))
        //promise实例状态失败,执行之前存储成功方法的数组把传给reject的参数传进去
      }
    }
    try{
      excutor(resolve,reject) //如果没有错直接执行   
    } catch(error) {
        reject(error) //一旦发现错误,直接扔给reject函数
    }
}
//加上then的完整代码
 MyPromise.prototype.then = function(onFulfailled, onRejected){
   const self = this
    if(self.status==='fulfailled'){
     onFulfailled(self.value)
    }
    if(self.status==='rejected'){
     onRejected(self.reason)
    }
    if(self.staus==='pending'){ 
    //处于pending状态也就是promsie实例还在执行的时候,异步状态情况下
      self.onFulfailledCallbacks.push(onFulfailed)
      self.onRejectedCallbacks.push(onRejected)
    }
} 

我们测试一下

 //改造后,使用setimeout模拟异步
new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(test1) // 没加'',表示变量但这个变量我们没有定义过
    },1000)
}).then(res=>{
    console.log(res)
}, error=>{
    console.log(error)
}) //此时控制台输出ReferenceError: test1 is not defined,成功通过reject返回了错误信息

此版本只是根据个人理解实现的一个简单版本,跟原版peomise想去甚远,也还有很多功能没有实现,只是抛砖引玉一下,感兴趣的可以自己按照promiseA+规范去实现一下,网上也有一大堆现成的实现(本文github地址:github.com/chuanHH/Blo…),后续可能会出一个完整版的promise