美文网首页饥人谷技术博客
大白话 Promise 完整实现

大白话 Promise 完整实现

作者: 临安linan | 来源:发表于2019-11-09 12:29 被阅读0次

    首先,Promise 中有三种状态:pending(等待态)、fulfilled(完成态)、rejected(拒绝态),并且这只能从 pending 到 fulfilled 或者从 pending 到 rejected,并且此过程不可逆。

    我们先来实现 Promise 的基本结构:

    当我们 new 一个 Promise 时,会传入一个执行函数:new Promise((resolve, reject) => {})

    这个执行函数里有两个参数,resolve、reject 方法都是在 Promise 类里定义的,外部调用这个方法时,还可以往里面传入参数,Promise 拿到这个值后保存起来,以供 .then 调用

    .then 方法需要指定成功的回调和失败的回调,根据 state 状态的不同来执行对应的回调

    class Promise{
      constructor(executor){
        this.state = "pending"  // 初始为 pending 态
        this.value = ''
        this.reson = ''
        const resolve = (value) => {
          this.value = value  // 将传入的 value 保存起来,以便 then 调用
          if(this.state === 'pending'){ // 只能从 pending 变到 fulfilled
            this.state = 'fulfilled'  // resolve 后,将状态置为 fulfilled
          }
        }
        const reject = (reson) => {
          this.reson = reson
          if(this.state === 'pending'){
            this.state = 'rejected'
          }
        }
    
        executor(resolve, reject)
      }
      then(onfulfilled, onrejected){
        if(this.state === 'fulfilled'){
          onfulfilled(this.value) // 将之前 resolve 的值传入
        }else if(this.state === 'rejected'){
          onrejected(this.reson)  // 将之前 reject 的值传入
        }
      }
    }
    

    以上,我们实现了两个功能:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调

    但是,上面的 Promise 还不能支持异步代码。

    因为当 then 执行时,只对 fulfilled、rejected 两种状态做了判断,如果代码是异步的话,当执行到 then 方法时,resolve、reject 方法还没执行,还是 pending 态,所以我们这里需要对 pending 态做处理:

    暂时将回调保存起来,等到 resolve、reject 执行的时候,才去执行对应的回调

    class Promise{
      constructor(executor){
        this.state = "pending"
        this.value = ''
        this.reson = ''
        this.onfulfilledCallbacks = []  // 存放 .then 中成功的回调
        this.onrejectedCallbacks = [] // 存放 .then 中失败的回调
        const resolve = (value) => {
          this.value = value
          if(this.state === 'pending'){
            this.state = 'fulfilled'
          }
          this.onfulfilledCallbacks.forEach(fn => fn(this.value)) // 当 resolve 执行时,执行 then 中指定的成功回调
        }
        const reject = (reson) => {
          this.reson = reson
          if(this.state === 'pending'){
            this.state = 'rejected'
            this.onrejectedCallbacks.forEach(fn => fn(this.reson))
          }
        }
    
        executor(resolve, reject)
      }
      then(onfulfilled, onrejected){
        if(this.state === 'fulfilled'){
          onfulfilled(this.value)
        }else if(this.state === 'rejected'){
          onrejected(this.reson)
        }else if(this.state === 'pending'){ // 当 state 还未变化时,先将成功和失败的回调存起来
          this.onfulfilledCallbacks.push(onfulfilled)
          this.onrejectedCallbacks.push(onrejected)
        }
      }
    }
    

    以上,我们已经实现了:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调
    3. 支持 Promise 异步

    但是目前我们的 .then 只能调用一次,还不能实现链式调用 .then,要实现也很简单,只需要让 then 方法返回一个 promise 实例即可。

    但是注意:这里返回的 promise 实例必须是一个全新的 promise,这样才能保证后续 then 中的状态可以改变,不然的话从 newPromise 之后状态就一直保持成一个了。。。

    同时,then 的回调执行的返回值还会传给下一个 then,所以还要把返回值 resolve 出去

    then(onfulfilled, onrejected){
      const promise2 = new Promise((resolve, reject) => {
        if(this.state === 'fulfilled'){
          const x = onfulfilled(this.value)
          resolve(x)
        }else if(this.state === 'rejected'){
          const r = onrejected(this.reson)
          resolve(r)
        }else if(this.state === 'pending'){
          this.onfulfilledCallbacks.push((value) => {
            const x = onfulfilled(value)
            resolve(x)  // 将回调执行的返回值 resolve 出去,resolve 的值会挂载在返回的新的 promise 实例上的 value 属性上,也就是调用了这个实例里的 resolve 方法。下一个 then(也就是返回的这个新 promise 实例的 then) 可以拿到这个值
          })
          this.onrejectedCallbacks.push((reson) => {
            const r = onrejected(reson)
            resolve(r)  // 注意:即使是失败的回调,回调执行完成后,下一个状态依然是 fulfilled,除非出错!(后面我们将处理这个问题)
          })
        }
      })
    
      return promise2 // 返回一个全新的 promise 实例
    }
    

    以上,我们已经实现了:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调
    3. 支持 Promise 异步
    4. 每个 then 返回一个新 promise 实例,同时将回调执行的返回值 resolve 出去

    我们现在来考虑一个问题,上面 then 中将回调执行的返回值 resolve 直接出去了,如果返回值是一个普通的值的话,这样没有问题。

    但是,假如是个 promise,那就不能这样直接 resolve

    如果返回值是个 promise 实例,我们就必须调用这个 promisethen,让这个 promise 执行,拿到这个 promise resolve 的值

    我们需要写一个方法来处理返回值

    then(onfulfilled, onrejected){
      const promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {  // 这里加定时器,为了让里面的代码异步,保证传入 promise2 的时候, promise2 已经初始化完了,这也就解释了为什么 Promise 的 then 方法是异步的 
          if(this.state === 'fulfilled'){
            const x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          }
          
          else if(this.state === 'rejected'){
            const r = onrejected(this.reson)
            resolvePromise(promise2, r, resolve, reject)
          }
          
          else if(this.state === 'pending'){
            this.onfulfilledCallbacks.push((value) => {
              const x = onfulfilled(value)
              resolvePromise(promise2, x, resolve, reject) // 用特定的方法处理返回值
            })
            this.onrejectedCallbacks.push((reson) => {
              const r = onrejected(reson)
              resolvePromise(promise2, r, resolve, reject)
            })
          }
        }, 0)
      })
    
      return promise2
    }
    
    
    function resolvePromise(promise2, x, resolve, reject) {
      // 判断 x,如果是一般值直接 resolve,如果是一个 promise,要等待 promise resolve 或 reject
      if (promise2 === x) {
        return new TypeError('循环引用!')
      }
    
      else if (x instanceof Promise) {  // x 是 promise 实例
        x.then(y => {
          resolvePromise(promise2, y, resolve, reject)  // 此时的 y 可能还是一个 promise,所以需要递归调用 resolvePromise,直到解析出一个普通值,就将这个值通过 "传进来的 promise2 的 resolve 方法" 传出去
        }, r => {
          reject(r) // x 内部 reject 了或出错了
        })
      }
      
      else { // 普通值,直接 resolve
        resolve(x)
      }
    }
    

    这段代码需要注意的地方比较多:

    1. 为了保证 promise2 可以拿到,加了一个定时器使代码异步
    2. 对 then 中所有 resolve 的值都需要采用 resolvePromise 方法,因为回调函数的返回值可能是 promise 实例
    3. resolvePromise 方法中,需要校验是否循环引用,同时需要注意 promise resolve 的值还是 promise 的情况

    以上,我们实现的功能有:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调
    3. 支持 Promise 异步
    4. 每个 then 返回一个新 promise 实例,对回调执行的返回值进行判断,如果是普通值就直接 resolve 出去,如果是一个 promise 实例,就执行 then 方法,直到得到一个普通值。同时,我们让 then 方法变成异步的了

    接下来要考虑的就是,当我们第一次 new Promise 的时候,如果 resolve 的值也是一个 promise,也需要等待这个 promise 执行完 then,如下:

    new Promise((resolve, reject) => {
      resolve(Promise.resolve(123))
    })
    .then(d => {
      console.log(d)  // 123
    })
    

    所以我们需要对 resolve 方法进行完善

    const resolve = (value) => {
      if(value instanceof Promise){// 第一次 new Promise 时,resolve 的值如果是一个 promise
        // 调用 then,传入 resolve,resolve 中又进行是否为 promise 的判断调用,递归调用直到 value 不是一个 promise
        return value.then(resolve, reject)
      }
      if (this.state === 'pending') { // 只有 pedding 态可修改,并且不可逆
        this.state = 'fulfilled'
        this.value = value
        this.onfulfilledCallbacks.forEach(fn => fn(value)) // 执行 resolve 回调
      }
    }
    

    以上,我们实现的功能有:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调
    3. 支持 Promise 异步
    4. 每个 then 返回一个新 promise 实例,对回调执行的返回值进行处理。同时,让 then 方法变成异步
    5. 对第一次 new Promise 时 resolve 的值进行 Promise / 非Promise 的处理

    回顾一下我们上面的代码,好像一直没有出现 reject 的情况,在 Promise 中,出现 reject 的情况有几种:

    1. 发生错误
    2. 主动 reject

    我们对代码中需要进行错误捕获的地方做一下处理,顺便把 catch 方法实现了

    class Promise {
      constructor(executor) {
        this.state = "pending"
        this.value = ''
        this.reson = ''
        this.onfulfilledCallbacks = []
        this.onrejectedCallbacks = [] 
        const resolve = (value) => {
          if (value instanceof Promise) {
            return value.then(resolve, reject)
          }
          if (this.state === 'pending') {
            this.state = 'fulfilled'
            this.value = value 
            this.onfulfilledCallbacks.forEach(fn => fn(value))
          }
        }
        const reject = (reson) => {
          this.reson = reson
          if (this.state === 'pending') {
            this.state = 'rejected'
            this.onrejectedCallbacks.forEach(fn => fn(this.reson))
          }
        }
    
        try { // 错误捕获
          executor(resolve, reject)
        } catch (e) {
          reject(e)
        }
      }
      then(onfulfilled, onrejected) {
        const promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            if (this.state === 'fulfilled') {
              try { // 错误捕获
                const x = onfulfilled(this.value)
                resolvePromise(promise2, x, resolve, reject)
              } catch (e) {
                reject(e)
              }
            }
    
            else if (this.state === 'rejected') {
              try { // 错误捕获
                const r = onrejected(this.reson)
                resolvePromise(promise2, r, resolve, reject)
              } catch (e) {
                reject(e)
              }
            }
    
            else if (this.state === 'pending') {
              this.onfulfilledCallbacks.push((value) => {
                try { // 错误捕获
                  const x = onfulfilled(value) 
                  resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              }) 
              this.onrejectedCallbacks.push((reson) => {
                try { // 错误捕获
                  const r = onrejected(reson) 
                  resolvePromise(promise2, r, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              })
            }
          },0)
        })
    
        return promise2
      }
      catch(rejectFn){  // 错误处理函数
        return this.then(null, rejectFn)  // catch 其实就是第一个参数为 null 的 then 方法
      }
    }
    
    function resolvePromise(promise2, x, resolve, reject) {
    
      if (promise2 === x) {
        return new TypeError('循环引用!')
      }
    
      else if (x instanceof Promise) {
        x.then(y => {
          resolvePromise(promise2, y, resolve, reject)
        },
        r => {
          reject(r)
        })
      }
    
      else {
        resolve(x)
      }
    }
    

    我们已经基本完善我们的 Promise 了,不过还需要考虑一种极端情况:

    假如我中间传了一个空的 then,new Promise 中 resolve 的值仍然可以被传递下去,被下一个 then 拿到并打印。

    new Promise((resolve, reject) => {
      resolve(123)
    })
    .then()
    .then(v => {
      console.log(v)
    })
    

    这就需要给 then 传一个默认的函数参数,不传的话默认将拿到的值 return 出去

    很简单,一行搞定

    then(onfulfilled = v=>v, onrejected = r=>r){
      // ...
    }
    

    以上,我们的 Promise 就实现得差不多了,回顾一下我们实现的思路:

    1. 实现了 Promise 基本结构和状态切换
    2. 实现基本的 then 方法,根据状态判断执行的回调
    3. 支持 Promise 异步
    4. 每个 then 返回一个新 promise 实例,对回调执行的返回值进行 Promise / 非Promise 的处理。同时,让 then 方法变成异步
    5. 对第一次 new Promise 时 resolve 的值进行 Promise / 非Promise 的处理
    6. 给 Promise 内部添加错误处理和 catch 方法
    7. 给 then 设置默认参数,不传的时候默认把值传递下去

    还剩下一些 Promise 类的静态方法,这里我们也一并实现了:

    Promise.resolve = function(value){
      //  Promise.resolve 实际上就是创建一个新的 Promise 实例返回,同时将传入的 value 参数 resolve 出去
      return new Promise((resolve, reject) => {
        resolve(value)
      })
    }
    Promise.reject = function(reson){
      return new Promise((resolve, reject) => { // Promise.reject 原理同上
        reject(reson)
      })
    }
    

    这里着重讲一下 Promise.allPromise.race 方法

    1. Promise.all:
      Promise.all 接收一个任务数组,数组元素 (可能是 promise 或其他值) 并发执行,如果是 Promise 就执行他,拿到 resolve 的值,如果是其他普通值就直接存起来,执行完成后的值存放在一个结果数组

      Promise.all 执行后会返回一个 新的 Promise 实例,并且会将结果数组 resolve 出去,下一个 then 中可以拿到

    很明显,我们只需要在内部实现一个计数器,每个任务元素完成后将计数器加 1,只要达到了任务数组的 length 长度即可

    1. Promise.race
      这个方法就是:比比谁最快执行完

      遍历数组参数,执行每一个元素 (同样的,注意区分 Promise 和 非Promise)

      对于 Promise 实例,Promise 执行完成后,直接 resolve

      对于普通值,直接 resolve
    Promise.all = function (p) {
      return new Promise((resolve, reject) => {
        let count = 0
        const result = []  // 结果数组
    
        function processData(index, value) {  // 存放每个 任务执行完后的值,并计数,计数完成后将 result 数组 resolve 出去
          result[index] = value
          if (++count === p.length) {
            resolve(result)
          }
        }
        p.forEach((cur, index) => {
          if (cur instanceof Promise) { // promise 实例
              cur.then(v => {
                processData(index, v)
              }, r => {
                reject(r) // 只要任何一个 promise 出错,就 reject
              })
          } else { // 普通值
            processData(index, cur)
          }
        })
      })
    }
    
    Promise.race = function (p) {
      return new Promise((resolve, reject) => { // 只要一个完成了,直接 resolve,resolve 出来的值就是最快执行完的
        p.forEach(cur => {
          if(cur instanceof Promise){
            cur.then(r => { // 执行 promise 然后 resolve 
              resolve(r)
            })
          }else { // 不是 promise,直接 resolve
            resolve(cur)
          }
        })
      })
    }
    

    以上就完成了对 Promise 的编写,完整代码如下 (无注释版):

    class Promise {
      constructor(executor) {
        this.state = "pending"
        this.value = ''
        this.reson = ''
        this.onfulfilledCallbacks = []
        this.onrejectedCallbacks = [] 
        const resolve = (value) => {
          if (value instanceof Promise) {
            return value.then(resolve, reject)
          }
          if (this.state === 'pending') {
            this.state = 'fulfilled'
            this.value = value 
            this.onfulfilledCallbacks.forEach(fn => fn(value))
          }
        }
        const reject = (reson) => {
          this.reson = reson
          if (this.state === 'pending') {
            this.state = 'rejected'
            this.onrejectedCallbacks.forEach(fn => fn(this.reson))
          }
        }
    
        try {
          executor(resolve, reject)
        } catch (e) {
          reject(e)
        }
      }
      then(onfulfilled = v=>v, onrejected = r=>r) {
        const promise2 = new Promise((resolve, reject) =>{
          setTimeout(() =>{
            if (this.state === 'fulfilled') {
              try {
                const x = onfulfilled(this.value)
                resolvePromise(promise2, x, resolve, reject)
              } catch (e) {
                reject(e)
              }
            }
    
            else if (this.state === 'rejected') {
              try {
                const r = onrejected(this.reson)
                resolvePromise(promise2, r, resolve, reject)
              } catch (e) {
                reject(e)
              }
            }
    
            else if (this.state === 'pending') {
              this.onfulfilledCallbacks.push((value) =>{
                try {
                  const x = onfulfilled(value) 
                  resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              }) 
              this.onrejectedCallbacks.push((reson) =>{
                try {
                  const r = onrejected(reson) 
                  resolvePromise(promise2, r, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              })
            }
          },0)
        })
    
        return promise2
      }
      catch(rejectFn){
        return this.then(null, rejectFn)
      }
    
    }
    function resolvePromise(promise2, x, resolve, reject) {
    
      if (promise2 === x) {
        return new TypeError('循环引用!')
      }
    
      else if (x instanceof Promise) {
        x.then(y => {
          resolvePromise(promise2, y, resolve, reject)
        },
        r => {
          reject(r)
        })
      }
    
      else {
        resolve(x)
      }
    }
    Promise.resolve = function(value){
      return new Promise((resolve, reject) => {
        resolve(value)
      })
    }
    Promise.reject = function(reson){
      return new Promise((resolve, reject) => {
        reject(reson)
      })
    }
    Promise.all = function (p) {
      return new Promise((resolve, reject) => {
        let count = 0
        const result = []
    
        function processData(index, value) {
          result[index] = value
          if (++count === p.length) {
            resolve(result)
          }
        }
        p.forEach((cur, index) => {
          if (cur instanceof Promise) {
              cur.then(v => {
                processData(index, v)
              }, r => {
                reject(r)
              })
          } else {
            processData(index, cur)
          }
        })
      })
    }
    Promise.race = function (p) {
      return new Promise((resolve, reject) => {
        p.forEach(cur => {
          if(cur instanceof Promise){
            cur.then(r => {
              resolve(r)
            })
          }else { 
            resolve(cur)
          }
        })
      })
    }
    

    感谢你看到这里 ()

    欢迎关注更多个人博客

    本文正在参与“写编程博客瓜分千元现金”活动,关注公众号“饥人谷”回复“编程博客”参与活动。

    相关文章

      网友评论

        本文标题:大白话 Promise 完整实现

        本文链接:https://www.haomeiwen.com/subject/dwzgbctx.html