为了账号安全,请及时绑定邮箱和手机立即绑定

用最通俗的语言,由浅入深一步一个脚印,自己手写一个promise

标签:
JavaScript

本文有点长。。

先看一个最简陋的promise

let p=new Promise(function(resolev,reject){
    console.log('执行')
  })
这时会在浏览器打出 “执行”.... 从这里可以推断出promise的结构应该是:
定义promise,传递一个参数,这个参数 是一个函数 并包含两个方法 resolve,reject,这个函数在promise内部是立即执行的
function Promise(executor){
    function resolve(){ }
    function reject(){ }
    //其实可以把这个executor看成是一个自执行函数
    /*
    类似
        (function(resolev,reject){
            console.log('执行') 
        })(resolve,reject)
    */
    executor(resolve,reject)
}

promise有三种状态, pending(等待状态),resolved(成功状态),rejected(失败状态),而且状态一经改变就不会再变~所以只会有两种情况
pending——————>resolved 等待——————>成功 成功了就不能失败...
pending——————>rejected 等待——————>失败 失败了就不能成功...ರ_ರ
//原生promise
let p=new Promise(function(resolev,reject){
    //只会调用最前面的这个
    resolve(100)
    rejecte(200)
  })
  p.then(function(data){
      console.log(data,'resolved')
  },function(error){
      console.log(error,'rejecte')
  })
原生promise中,resolve和reject 只会调用其中在最前面的一个。。并且调用resolve会执行then中的第一个回调,调用reject会执行then中的第二个回调
要实现这样的效果需要在我们自己的promise函数内
  • 定义 status,存储当前的状态
  • 定义 value, 存储resolve成功 调用时传过来的值
  • 定义 reason,存储 reject失败 调用的报错信息
  • 定义一个then函数,并接收两个类型为函数的参数,第一个是成功的回调,第二个是失败的回调
  • 从promise中的 then方法可以看出~这两个方法只有一个会被执行


    function Promise(executor) {
    let self = this
    this.status = 'pending' //当前状态
    this.value = undefined  //存储成功的值
    this.reason = undefined //存储失败的原因
    function resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
      }
    }
    executor(resolve, reject)
    }
    Promise.prototype.then = function (infulfilled, inrejected) {
    //由status決定,只有一个方法会被执行
    if (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
    }

    现在这个promise已经有点样子了..但是离完善还差了很远~因为promise的亮点就是解决异步回调地狱的问题,但是这个手写的promise只支持同步代码,例子如下:

    //用了自己的promise~不是原生的~~~!
    let p = new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve(100)
    },1000)
    })
    //控制台没有打印任何东西
    p.then(function (data) {
    console.log(data, 'resolve')
    }, function (error) {
    console.log(error, 'reject')
    })

    可以看到,这里的then方法没有执行,为什么呢~~~

    其实已经执行过了..只是因为现在的状态还是pending等待态。。。并不满足两个if条件里的任何一个。。所以直接略过了,
    现在来分析下为什么会这样呢
    • 当我们 new Promise(function (resolve, reject) { setTimeout(() => { resolve(100) },1000) }) 的时候 executor()会立即执行,不过别忘了这有个 setTimeout,所以executor()里面的动作 需要等1000ms
    • 但是~由于同步函数比异步函数快的原因,所以这时then方法开始执行了,然而这时 Promise内部的status仍然是pending等待状态,then方法扫描了一下内部...发现status不满足任何一个条件,so..it's ending...结束
    • 1000ms过后,executor()中的resolve开始执行了, 它把status改成了resolved,把值赋给了value..然而并没有后续动作了,so..it's ending...

让自己的promise支持异步调用

  • 在 promise中定义两个数组 onResovedCallbacks,和 onRejectedCallbacks,用来存放成功和失败的回调函数
  • 在then中判断status 如果是 pending等待状态 ,那就把成功的回调和失败的回调分别存到上面两个数组内
  • 在promise的resolve和reject方法中分别遍历onResovedCallbacks和onRjectedCallbacks,执行其中的方法,代码如下

    function Promise(executor) {
    let self = this
    this.status = 'pending' //当前状态
    this.value = undefined  //存储成功的值
    this.reason = undefined //存储失败的原因
    this.onResolvedCallbacks = []//存储成功的回调
    this.onRejectedCallbacks = []//存储失败的回调
    function resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
        //遍历,执行其中的infulfilled()方法
        self.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
        //遍历,执行其中的inRejected()方法
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    executor(resolve, reject)
    }
    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    if (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
    if (this.status == 'pending') {
       //这时既不是resolved也是不是rejected状态
      this.onResolvedCallbacks.push(function () {
        infulfilled(self.value)
      })
      this.onRejectedCallbacks.push(function () {
        inrejected(self.reason)
      })
    }
    }
    let p = new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve(100)
    }, 2000)
    })
    p.then(function (data) {
    console.log(data, 'resolve')
    }, function (error) {
    console.log(error, 'reject')
    })

    现在可以试一下setTimeout ,嗯~已经支持异步调用了~

    不过还有一种情况,就是当new Promise()中抛出了异常,例如
    let p = new Promise(function (resolve, reject) {
    throw new Error('错误')
    })
    p.then(function (data) {
    console.log(data, 'resolve')
    }, function (error) {
    console.log(error, 'reject')
    })

    这种情况下 then方法中,成功和失败的回调函数也不会被调用,只会报个错,因为现在的status还是pending那就需要我们在promise函数内部捕获一下异常 :

    function Promise(executor) {
    let self = this
    this.status = 'pending' //当前状态
    this.value = undefined  //存储成功的值
    this.reason = undefined //存储失败的原因
    this.onResolvedCallbacks = []//存储成功的回调
    this.onRejectedCallbacks = []//存储失败的回调
    function resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
        self.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    //捕获到异常就直接失败了...
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
    
    }
    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    if (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
    if (this.status == 'pending') {
      this.onResolvedCallbacks.push(function () {
        infulfilled(self.value)
      })
      this.onRejectedCallbacks.push(function () {
        inrejected(self.reason)
      })
    }
    }
    let p = new Promise(function (resolve, reject) {
    throw new Error('错误')
    })
    p.then(function (data) {
    console.log(data, 'resolve')
    }, function (error) {
    console.log(error, 'reject111')
    })
    现在已经可以成功捕获异常了

    现在手写的promise离原生的promise又更像了一点,但是原生最大的特点~链式调用,我们还没有实现,

    分析下~链式调用的实现方法

    为了保证链式调用,不管是成功/失败/等待状态,都要返回一个新的Promise才能继续调用then方法

    所以声明一个promise2=新的Promise去 解析,当new Promise的时候会立即执行executor

    function Promise(executor) {
    let self = this
    this.status = 'pending' //当前状态
    this.value = undefined  //存储成功的值
    this.reason = undefined //存储失败的原因
    this.onResolvedCallbacks = []//存储成功的回调
    this.onRejectedCallbacks = []//存储失败的回调
    function resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
        self.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
    }
    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    if (this.status == 'resolved') {
      promise2 = new Promise(function (resolve, reject) {
        infulfilled(self.value)
      })
    }
    if (this.status == 'rejected') {
      promise2 = new Promise(function (resolve, reject) {
        inrejected(self.reason)
      })
    }
    if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function () {
          infulfilled(self.value)
        })
        self.onRejectedCallbacks.push(function () {
          inrejected(self.reason)
        })
      })
    }
    return promise2
    }

    这段代码看起来很简单~嗯~~~就是返回一个新的promise嘛~

    其次~原生promise规定在then中无论是成功的回调还是失败的回调,只要返回了结果就会进入下一个then的成功的回调,如果有异常就会进入下一个then的失败的回调.

    先来看一下then在抛出异常后的处理

    *** 先看下面一段代码

    //原生的promise和现在手写的promise都能实现这个功能哦~
    let p = new Promise(function (resolve, reject) {
    resolve(1000)
    //reject(2000)
    })
    let p2 = p.then(function (data) {
    throw new Error('錯誤')
    }, function (error) {
    throw new Error('失败了')
    })
    p2.then(function (data) {
    console.log(data, 'resolve');
    }, function (error) {
    console.log(error, 'rejected');
    })

    可以看到,无论是在promise实例中使用了 resolve()还是reject()只要,在then中抛出了异常,就会进入下一个then的失败的回调

    一步步分解下这个捕获异常的执行流程,先上个图

    捋一下图片里的思路
  • 第一步
    //先调用了resolve,所以现在的promise的status是resolved 成功态
    let p = new Promise(function (resolve, reject) {
    resolve(1000)
    })
  • 第二步 调用then方法,then方法中 根据当前的状态 resolved进入第一个if条件中
  • 2.1 新初始化了一个Promise实例
  • 2.2 新的Promiese实例会立即执行 infulfilled被调用,但是现在infulfilled里抛出了异常,被新的promise try catch捕获到,然后reject方法被调用,新的promise状态 status被改成reject失败状态, reason等于被catch到的报错信息
    let p2 = p.then(function (data) {
    throw new Error('錯誤')
    }, function (error) {
    throw new Error('失败了')
    })
  • 第三步, 现在 p2就是那个新的promise,并且状态是reject reason是刚捕获到的报错信息,当它调用then方法时进入第二个if条件,因为当前状态是reject嘛,
  • 3.1 新初始化一个Promise实例
  • 3.2 Promise立即执行,inrejected方法被调用,即p2.then的第二个回调函数执行。。打印错误信息
    p2.then(
    function (data) {
      console.log(data, 'resolve');
    },
    function (error) {
      console.log(error, 'rejected');
    })
    终于把异常处理分析完了。。。
    现在来分析下成功,或者失败的返回值的情况,

    原生的primse执行then方法,有时可能会返回一个结果,作为下一个promise的值value或失败的原因reason,这个结果可能是一个普通值也可能是一个promise

    let p = new Promise(function (resolve, reject) {
    resolve(1000)
    })
    let p2 = p.then(function (data) {
    //返回一个普通值
    // return 'test'
    //返回一个promise
    return new Promise(function (resolve, reject) {
      //resolve('成功')
      reject('失败')
    })
    }, function (error) {
    throw new Error('failed')
    })
    p2.then(
    function (data) {
      console.log(data, 'resolveq');
    },
    function (error) {
      console.log(error, 'rejected ');
    })

    为了实现这个效果,要完善一下手写的Promisethen方法,写一个函数resolvePromise统一处理then方法执行后返回的结果

    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    if (this.status == 'resolved') {
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        let x = infulfilled(self.value)
        //这里的resolve和reject都是promise2的,x就是infulfilled的返回值 可能是一个promise,也可能是个普通值所以要统一处理
        resolvePromise(promise2, x, resolve, reject)
      })
    }
    if (this.status == 'rejected') {
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        let x = inrejected(self.reason)
        //这里的resolve和reject都是promise2的,x就是infulfilled的返回值 可能是一个promise,也可能是个普通值所以要统一处理
        resolvePromise(promise2, x, resolve, reject)
      })
    }
    if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          let x = infulfilled(self.value)
        //这里的resolve和reject都是promise2的,x就是infulfilled的返回值 可能是一个promise,也可能是个普通值所以要统一处理
          resolvePromise(promise2, x, resolve, reject)
        })
        self.onRejectedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          let x = inrejected(self.reason)
        //这里的resolve和reject都是promise2的,x就是infulfilled的返回值 可能是一个promise,也可能是个普通值所以要统一处理
          resolvePromise(promise2, x, resolve, reject)
        })
      })
    }
    return promise2
    }

    resolvePromise的内部实现

  • 1.首先看一种情况,
    在原生Promise中 then方法返回的结果可能是他本身,这种情况永远不会成功或者失败(循环引用),这时会抛出一个类型错误的异常

    所以在 resolvePromise内部要判断 返回的结果和Promise是不是同一个
  • 2.判断 x是不是一个promise,如果是promise,如果x是对象,并且x的then方法是函数,那它就是promise
  • 3 如果x不是promise 那应该是普通值 直接resolve
  • 4.如果x是一个promise,那就看下他有没有then方法

    function resolvePromise(p2, x, resolve, reject) {
    if (p2 === x && x != undefined) {
      reject(new TypeError('类型错误'))
    }
    //可能是promise,看下对象中是否有then方法,如果有~那就是个promise
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {//为了防止出现 {then:11}这种情况,需要判断then是不是一个函数
        let then = x.then
        if (typeof then === 'function') {
          then.call(x, function (y) {
            //y 可能还是一个promise,那就再去解析,知道返回一个普通值为止
            resolvePromise(p2, y, resolve, reject)
          }, function (err) {
            reject(err)
          })
        } else {//如果then不是function 那可能是对象或常量
          resolve(x)
        }
      } catch (e) {
        reject(e)
      }
    } else {//说明是一个普通值 
      resolve(x)
    }
    }

    再完善一下..我们的代码应该可以在then中什么都不传,实现值得穿透

    let p = new Promise(function (resolve, reject) {
    resolve(1000)
    })
    p.then().then().then(function (data) {
    console.log(data, 'resolve');
    }, function (error) {
    console.log(error, 'reject');
    })

    所以在then方法中需要对接收的参数infulfilled, inrejected进行容错 处理,
    对于infulfilled 有就用原参数,没有就给个默认的函数
    对于inrejected 没有就抛出异常

    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    //如果infulfilled不是function就给个默认函数,返回val
    infulfilled = typeof infulfilled === 'function' ? infulfilled : function (val) {
      return val
    }
    //如果inrejected不是函数就抛出异常
    inrejected = typeof inrejected === 'function' ? inrejected : function (err) {
      throw err
    }
    if (this.status == 'resolved') {
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        let x = infulfilled(self.value)
        resolvePromise(promise2, x, resolve, reject)
      })
    }
    if (this.status == 'rejected') {
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        let x = inrejected(self.reason)
        resolvePromise(promise2, x, resolve, reject)
      })
    }
    if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          let x = infulfilled(self.value)
          resolvePromise(promise2, x, resolve, reject)
        })
        self.onRejectedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          let x = inrejected(self.reason)
          resolvePromise(promise2, x, resolve, reject)
        })
      })
    }
    return promise2
    }

    最后~promise规范中要求所有的infulfilled, inrejected都要异步执行,所以这里给所有的infulfilled, inrejected加上setTimeout,下面是完整代码:

    function Promise(executor) {
    let self = this
    this.status = 'pending' //当前状态
    this.value = undefined  //存储成功的值
    this.reason = undefined //存储失败的原因
    this.onResolvedCallbacks = []//存储成功的回调
    this.onRejectedCallbacks = []//存储失败的回调
    function resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
        self.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
    }
    Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    infulfilled = typeof infulfilled === 'function' ? infulfilled : function (val) {
      return val
    }
    inrejected = typeof inrejected === 'function' ? inrejected : function (err) {
      throw err
    }
    if (this.status == 'resolved') {
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        setTimeout(function () {
          try {
            let x = infulfilled(self.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        });
    
      })
    }
    if (this.status == 'rejected') {
    
      promise2 = new Promise(function (resolve, reject) {
        //x可能是一个promise,也可能是个普通值
        setTimeout(function () {
          try {
            let x = inrejected(self.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        });
      })
    }
    if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          setTimeout(function () {
            try {
              let x = infulfilled(self.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
              reject(err)
            }
          });
        })
        self.onRejectedCallbacks.push(function () {
          //x可能是一个promise,也可能是个普通值
          setTimeout(function () {
            try {
              let x = inrejected(self.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
              reject(err)
            }
          });
        })
      })
    }
    return promise2
    }
    function resolvePromise(p2, x, resolve, reject) {
    if (p2 === x && x != undefined) {
      reject(new TypeError('类型错误'))
    }
    //可能是promise,看下对象中是否有then方法,如果有~那就是个promise
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      try {//为了防止出现 {then:11}这种情况,需要判断then是不是一个函数
        let then = x.then
        if (typeof then === 'function') {
          then.call(x, function (y) {
            //y 可能还是一个promise,那就再去解析,知道返回一个普通值为止
            resolvePromise(p2, y, resolve, reject)
          }, function (err) {
            reject(err)
          })
        } else {//如果then不是function 那可能是对象或常量
          resolve(x)
        }
      } catch (e) {
        reject(e)
      }
    } else {//说明是一个普通值 
      resolve(x)
    }
    }

    ok~promise写完了。。欢迎大家提出建议。。

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消