最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 手写 Promise

    正文概述 掘金(lyn-ho)   2020-11-25   611

    Promise 雏形

    Promise 其实就是一个构造函数。

    它只有一个参数,按照 Promise/A+ 规范的命名,我们把 Promise 构造函数的参数叫做 executor,它是函数类型的参数。

    这个函数又“自动”具有 resolvereject 两个方法作为参数。

    function Promise(executor) {
    
    }
    

    Promise 构造函数返回一个 Promise 对象实例,这个返回的 Promise 对象具有一个 then 方法。

    then 方法中,调用者可以定义两个参数,分别是 onfulfilledonrejected ,它们都是函数类型参数。

    其中,onfulfilled 通过参数可以获取 Promise 对象经过 resolve 处理后的值,onrejected 可以获取 Promise 对象经过 reject 处理后的值。

    通过这个值,我们来处理异步操作完成后的逻辑。

    这些都是 Promise/A+ 规范的基本内容,接下来我们继续实现 Promise。

    • Promise 构造函数
    • 在构造函数基础上加上 then 原型方法
    function Promise(executor) {
    
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      
    }
    

    下面是一个示例:

    let promise1 = new Promise((resolve, reject) => {
      resolve('data')
    })
    
    promise1.then(data => {
      console.log(data)
    })
    
    let promise2 = new Promise((resolve, reject) => {
      reject('error')
    })
    
    promise2.then(data => {
      console.log(data)
    }, error => {
      console.log(error)
    })
    

    在使用 new 关键字调用 Promise 构造函数时,在合适的时机(往往是异步操作结束时)调用 executor 的参数 resolve,并将经过处理后的值作为 resolve 的函数参数执行,这个值便可以放在后续 then 方法的第一个函数参数 onfulfilled 中拿到。

    同理,在出现错误时,调用 executor 的参数 reject ,并将错误信息作为 reject 的函数参数执行,这个错误信息可以在后续 then 方法的第二个函数参数 onrejected 中拿到。

    因此,我们在实现 Promise 时,应该有两个变量,分别存储经过 resolve 处理后的值,以及经过 reject 处理后的值

    同时还需要一个变量来存储状态,这个状态就是 Promise 实例的状态(pending, fulfilled, rejected

    最后需要提供 resolve 方法和 reject 方法,这两个方法需要作为 executor 的参数提供给开发者使用

    如下:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      const self = this
    
      this.status = PENDING
      this.value = null
      this.reason = null
    
      function resolve(value) {
        self.value = value
      }
    
      function reject(reason) {
        self.reason = reason
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
      onfulfilled(this.value)
    
      onrejected(this.reason)
    }
    

    为了保证 onfulfilledonrejected 的健壮性,我们为它们设置了默认值,其默认值为一个函数元(Function.prototype

    因为 resolve 的最终调用是由开发者在不确定环境下(往往是在全局中)直接调用的,因此为了在 resolve 函数中能够拿到 Promise 实例的值,我们需要对 this 另存,或者可以使用箭头函数来保证 this 执行的准确性

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      const resolve = value => {
        this.value = value
      }
    
      const reject = reason => {
        this.reason = reason
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
      onfulfilled(this.value)
    
      onrejected(this.reason)
    }
    

    为什么 then 要放在 Promise 构造函数的原型上,而不是放在构造函数内部呢?

    每个 Promise 实例的 then 方法逻辑都是一致的,实例在调用该方法时,可以通过原型 (Promise.proptype) 来调用,而不需要每次实例化都新创建一个 then 方法,以便节省内存

    Promise 完善状态

    判断以下代码的输出:

    let promise = new Promise((resolve, reject) => {
      resolve('data')
      reject('error')
    })
    
    promise.then(data => {
      console.log(data)
    }, error => {
      console.log(error)
    })
    

    正常的话,以上代码只会输出 data

    我们知道,Promise 实例的状态只能从 pending 变为 fulfilled 或者从 pending 变为 rejected

    状态一旦改变完成,就不可再次变化或逆转

    而我们上面的代码实现显然无法满足这一特性,执行上一段代码回输出 dataerror ,因此需要对状态进行判断和完善

    如下:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      const resolve = value => {
        if (this.status === PENDING) {
          this.value = value
          this.status = FULFILLED
        }
      }
    
      const reject = reason => {
        if (this.status === PENDING) {
          this.reason = reason
          this.status = REJECTED
        }
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      if(this.status === FULFILLED)
        onfulfilled(this.value)
    
      if(this.status === REJECTED)
        onrejected(this.reason)
    }
    

    resolvereject 方法中加入了判断,只允许 Promise 实例状态从 pending 变为 fulfilled 或者从 pending 变为 rejected

    Promise.proptype.then 的参数 onfulfilledonrejected 进行了判断,当实参不是函数类型时,就需要赋予默认函数值

    这样一来,上面的示例代码就可以顺利执行了

    但是,Promise 时用来解决异步问题的,而我们的代码全部都是同步执行的,似乎还差了更重要的逻辑。

    Promise 异步实现

    我们从下面的示例代码,逐步分析

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('data')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
    })
    

    正常来讲,上面代码应该在 1s 后输出 data ,但是现在,代码并没有输出任何信息。这是为什么?

    原因很简单,因为我们的实现逻辑都是同步的。上面代码在实例化 Promise 的构造函数时,会在 setTimeout 逻辑中调用 resolve ,也就是说在 1s 后才会调用 resolve 方法,更改 Promise 实例状态。而结合我们的实现,then 方法中的 onfulfilled 是同步执行的,它在执行时 this.status 仍是 pending ,并没有做到 “ 1s 后再执行 onfulfilled

    我们应该在合适的时间去调用 onfulfilled 方法,这个合适的时间应该是开发者调用 resolve 的时刻,那么我们先在 statuspending 时把开发者传进来的 onfulfilled 方法存起来,再在 resolve 方法中执行即可

    如下:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      this.onFulfilledFunc = Function.prototype
      this.onRejectedFunc = Function.prototype
    
      const resolve = value => {
        if (this.status === PENDING) {
          this.value = value
          this.status = FULFILLED
    
          this.onFulfilledFunc(this.value)
        }
      }
    
      const reject = reason => {
        if (this.status === PENDING) {
          this.reason = reason
          this.status = REJECTED
    
          this.onRejectedFunc(this.reason)
        }
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      if (this.status === FULFILLED) {
        onfulfilled(this.value)
      }
    
      if (this.status === REJECTED) {
        onrejected(this.reason)
      }
    
      if (this.status === PENDING) {
        this.onFulfilledFunc = onfulfilled
        this.onRejectedFunc = onrejected
      }
    }
    

    通过测试发现,我们实现的代码可以支持异步执行了。

    我们知道 Promise 的 then 方法是异步执行的。再看一个?

    let promise = new Promise((resolve, reject) => {
      resolve('data')
    })
    
    promise.then(data => {
      console.log(data)
    })
    
    console.log(1)
    

    正常的话,上面的例子回按照顺序先输出 1,再输出 data

    而我们实现的代码没有考虑这种情况,实际先输出了 data 后输出 1 。因此,需要将 resolvereject 的执行放到任务队列中。这里我们姑且将它们放到 setTimeout 中,保证异步执行(这样的做法并不严谨,为了保证 Promise 属于微任务,很多 Promise 的实现库用了 MutationObserver 来模仿 nextTick

      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject)
        }
    
        setTimeout(() => {
          if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
    
            this.onFulfilledFunc(this.value)
          }
        }, 0);
      }
    
      const reject = reason => {
        setTimeout(() => {
          if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
    
            this.onRejectedFunc(this.reason)
          }
        }, 0);
      }
    
      executor(resolve, reject)
    

    这样一来,在执行到 executor(resolve, reject) 时,也能保证在 nextTick 中才会执行 Promise 被决议后的任务,不会阻塞同步任务

    同时,我们在 resovle 方法中加入了对 value 值是否为一个 Promise 实例的判断语句

    下面是目前的完整代码:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      this.onFulfilledFunc = Function.prototype
      this.onRejectedFunc = Function.prototype
    
      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject)
        }
    
        setTimeout(() => {
          if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
    
            this.onFulfilledFunc(this.value)
          }
        }, 0);
      }
    
      const reject = reason => {
        setTimeout(() => {
          if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
    
            this.onRejectedFunc(this.reason)
          }
        }, 0);
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      if (this.status === FULFILLED) {
        onfulfilled(this.value)
      }
    
      if (this.status === REJECTED) {
        onrejected(this.reason)
      }
    
      if (this.status === PENDING) {
        this.onFulfilledFunc = onfulfilled
        this.onRejectedFunc = onrejected
      }
    }
    

    Promise 细节完善

    在 Promise 实例状态变更之前添加多个 then 方法

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('data')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(`1: ${data}`)
    })
    promise.then(data => {
      console.log(`2: ${data}`)
    })
    

    正常情况,上面的代码回输出

    1: data
    2: data
    

    而我们的实现只会输出 2: data ,这是因为第二个 then 方法中的 onFulfilledFunc 会覆盖第一个 then 方法中的 onFulfilledFunc

    为了解决这个问题,只需要将所有 then 方法中的 onFulfilledFunc 存储到一个数组 onFulfilledArray 中,在当前 Promise 被决议时依次执行 onFulfilledArray 数组内的方法即可。对于 onRejectedFunc 同理

    改动后的代码:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      this.onFulfilledArray = []
      this.onRejectedArray = []
    
      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject)
        }
    
        setTimeout(() => {
          if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
    
            this.onFulfilledArray.forEach(func => {
              func(value)
            })
          }
        }, 0);
      }
    
      const reject = reason => {
        setTimeout(() => {
          if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
    
            this.onRejectedArray.forEach(func => {
              func(reason)
            })
          }
        }, 0);
      }
    
      executor(resolve, reject)
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      if (this.status === FULFILLED) {
        onfulfilled(this.value)
      }
    
      if (this.status === REJECTED) {
        onrejected(this.reason)
      }
    
      if (this.status === PENDING) {
        this.onFulfilledArray.push(onfulfilled)
        this.onRejectedArray.push(onrejected)
      }
    }
    

    另一个需要完善的细节,在构造函数中如果出错,将会自动出发 Promise 实例状态为 rejected 因此我们用 try...catch 块对 executor 进行包裹

      try {
        executor(resolve, reject)
      } catch (e) {
        reject(e)
      }
    

    Promise 链式调用

    先看一道题目:请判断以下代码的输出结果

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
      return `${data} next then`
    }).then(data => {
      console.log(data)
    })
    

    以上代码会在 1s 后输出 andy ,紧接着输出 andy next then

    我们看到,Promise 实例的 then 方法支持链式调用,输出经过 resolve 处理的值后,如果在 then 方法体的 onfulfilled 函数中同步显式返回新的值,则将会在新 Promise 实例 then 方法的 onfulfilled 函数中拿到新的值

    如果在第一个 then 方法体的 onfulfilled 函数中返回另一个 Promise 实例,结果又会如何

    看下面的代码:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(`${data} next then`)
        }, 2000);
      })
    }).then(data => {
      console.log(data)
    })
    

    上面代码会在 1s 后输出 andy 紧接再过 2s 输出 andy next then

    所以,一个 Promise 实例 then 方法的 onfulfilled 函数和 onrejected 函数是支持再次返回一个 Promise 实例的,也支持返回一个非 Promise 实例的普通值

    并且,返回的 Promise 实例或非 Promise 实例的普通值将会传给下一个 then 方法的 onfulfilled 函数或 onrejected 函数

    链式调用的初步实现

    为了能够支持 then 方法的链式调用,每一个 then 方法的 onfulfilled 函数和 onrejected 函数都应该返回一个 Promise 实例

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      this.onFulfilledArray = []
      this.onRejectedArray = []
    
      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject)
        }
    
        setTimeout(() => {
          if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
    
            this.onFulfilledArray.forEach(func => {
              func(value)
            })
          }
        }, 0);
      }
    
      const reject = reason => {
        setTimeout(() => {
          if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
    
            this.onRejectedArray.forEach(func => {
              func(reason)
            })
          }
        }, 0);
      }
    
      try {
        executor(resolve, reject)
      } catch (e) {
        reject(e)
      }
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      let promise2
    
      if (this.status === FULFILLED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onfulfilled(this.value)
              resolve(result)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === REJECTED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onrejected(this.reason)
              resolve(result)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === PENDING) {
        return promise2 = new Promise((resolve, reject) => {
          this.onFulfilledArray.push(() => {
            try {
              let result = onfulfilled(this.value)
              resolve(result)
            } catch (e) {
              reject(e)
            }
          })
    
          this.onRejectedArray.push(() => {
            try {
              let result = onrejected(this.reason)
              resolve(result)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    }
    

    这里要重点理解 this.status === PENDING 判断分支中的逻辑,这也是最难理解的。当使用 Promise 实例调用其 then 方法时,应该返回一个 Promise 实例,返回的就是 this.status === PENDING 判断分支中返回的 promise2 。那么这个 promise2 什么时候被决议呢?应该是在异步处理结束后,依次执行 onFulfilledArrayonRejectedArray 数组中的函数时

    onFulfilledArrayonRejectedArray 数组中的函数应该做些什么呢?

    需要切换 promise2 的状态,并进行决议

    链式调用的完善实现

    我们继续实现 then 方法,以便显式返回一个 Promise 实例。

    如下:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
    
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(`${data} next then`)
        }, 2000);
      })
    }).then(data => {
      console.log(data)
    })
    

    基于第一种 onfulfilled 函数和 onrejected 函数返回一个普通值的情况,要实现这种 onfulfilled 函数和 onrejected 函数返回一个 Promise 实例的情况并不困难。但是需要小幅重构一下代码,在之前实现的 let result = onfulfilled(this.value) 语句和 let result = onrejected(this.reason) 语句中,使变量 result 由一个普通值变为一个 Promise 实例。

    变量 result 既可以是一个普通值,也可以是一个 Promise 实例,为此我们抽象出 resulvePromise 方法进行统一处理。对已有实现进行改动后的代码。如下:

    const resolvePromise = (promise2, result, resolve, reject) => {
    
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      let promise2
    
      if (this.status === FULFILLED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onfulfilled(this.value)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === REJECTED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onrejected(this.reason)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === PENDING) {
        return promise2 = new Promise((resolve, reject) => {
          this.onFulfilledArray.push(() => {
            try {
              let result = onfulfilled(this.value)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
    
          this.onRejectedArray.push(() => {
            try {
              let result = onrejected(this.reason)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    }
    

    现在的任务就是实现 resolvePromise 函数,这个函数接收 4 个参数:

    • promise2:返回的 Promise 实例
    • resultonfulfilledonrejected 函数的返回值
    • resolvepromise2resolve 方法
    • rejectpromise2reject 方法
    const resolvePromise = (promise2, result, resolve, reject) => {
      // 当 result 和 promise 相等时,也就是在 onfulfilled 返回 promise2 时,执行 reject
      if (result === promise2) {
        reject(new TypeError('error due to circular reference'))
      }
    
      // 是否以及执行过 onfulfilled 或 onrejected
      let consumed = false
      let thenable
    
      if (result instanceof Promise) {
        if (result.status === PENDING) {
          result.then((data) => {
            resolvePromise(promise2, data, resolve, reject)
          }, reject)
        } else {
          result.then(resolve, reject)
        }
        return
      }
    
      let isComplexResult = (target => typeof target === 'function' || typeof target === 'object') && target !== null
    
      // 如果返回的时疑似 Promise 类型
      if (isComplexResult(result)) {
        try {
          thenable = result.then
    
          // 判断返回值是否是 Promise 类型
          if (typeof thenable === 'function') {
            thenable.call(result, function (data) {
              if (consumed) {
                return
              }
              consumed = true
    
              return resolvePromise(promise2, data, resolve, reject)
            }, function (error) {
                if (consumed) {
                  return
                }
                consumed = true
    
                return reject(error)
            })
          } else {
            resolve(result)
          }
        } catch (e) {
          if (consumed) {
            return
          }
          consumed = true
    
          return reject(e)
        }
      } else {
        resolve(result)
      }
    }
    

    resolvePromise 方法的第一步是对 “死循环” 进行处理,并在发生死循环时抛出错误

    手写 Promise

    怎么理解这个处理呢?Promise 规范中指出,其实出现 “死循环” 的情况如下:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(onfulfilled = data => {
      console.log(data)
      return onfulfilled(data)
    }).then(data => {
      console.log(data)
    })
    

    对于 onfulfilled 函数返回的结果 result :如果 result 不是 Promise 实例,不是对象,也不是函数,而是一个普通值(isCompexResult 函数用于对此进行判断),则直接对 promise2 进行决议

    对于 onfulfilled 函数返回的结果 result :如果 result 含有 then 属性方法,那么我们称该属性方法为 thenable ,说明 result 是一个 Promise 实例,当执行该实例的 then 方法(即 thenable )时,返回结果还可能是一个 Promise 实例类型,也可能是一个普通值,因此还要递归调用 resolvePromise

    如下:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(`${data} next then`)
        }, 2000);
      }).then(data => {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(`${data} next then`)
          }, 2000);
        })
      })
    }).then(data => {
      console.log(data)
    })
    

    上面代码会在 1s 后输出 andy ,再在 5s 后输出 andy next then next then

    现在的完整代码:

    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    function Promise(executor) {
      this.status = PENDING
      this.value = null
      this.reason = null
    
      this.onFulfilledArray = []
      this.onRejectedArray = []
    
      const resolve = value => {
        if (value instanceof Promise) {
          return value.then(resolve, reject)
        }
    
        setTimeout(() => {
          if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
    
            this.onFulfilledArray.forEach(func => {
              func(value)
            })
          }
        }, 0);
      }
    
      const reject = reason => {
        setTimeout(() => {
          if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
    
            this.onRejectedArray.forEach(func => {
              func(reason)
            })
          }
        }, 0);
      }
    
      try {
        executor(resolve, reject)
      } catch (e) {
        reject(e)
      }
    }
    
    const resolvePromise = (promise2, result, resolve, reject) => {
      // 当 result 和 promise 相等时,也就是在 onfulfilled 返回 promise2 时,执行 reject
      if (result === promise2) {
        reject(new TypeError('error due to circular reference'))
      }
    
      // 是否以及执行过 onfulfilled 或 onrejected
      let consumed = false
      let thenable
    
      if (result instanceof Promise) {
        if (result.status === PENDING) {
          result.then((data) => {
            resolvePromise(promise2, data, resolve, reject)
          }, reject)
        } else {
          result.then(resolve, reject)
        }
        return
      }
    
      let isComplexResult = (target => typeof target === 'function' || typeof target === 'object') && target !== null
    
      // 如果返回的时疑似 Promise 类型
      if (isComplexResult(result)) {
        try {
          thenable = result.then
    
          // 判断返回值是否是 Promise 类型
          if (typeof thenable === 'function') {
            thenable.call(result, function (data) {
              if (consumed) {
                return
              }
              consumed = true
    
              return resolvePromise(promise2, data, resolve, reject)
            }, function (error) {
                if (consumed) {
                  return
                }
                consumed = true
    
                return reject(error)
            })
          } else {
            resolve(result)
          }
        } catch (e) {
          if (consumed) {
            return
          }
          consumed = true
    
          return reject(e)
        }
      } else {
        resolve(result)
      }
    }
    
    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
    
      let promise2
    
      if (this.status === FULFILLED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onfulfilled(this.value)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === REJECTED) {
        return promise2 = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let result = onrejected(this.reason)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    
      if (this.status === PENDING) {
        return promise2 = new Promise((resolve, reject) => {
          this.onFulfilledArray.push(() => {
            try {
              let result = onfulfilled(this.value)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
    
          this.onRejectedArray.push(() => {
            try {
              let result = onrejected(this.reason)
              resolvePromise(promise2, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    }
    

    Promise 穿透实现

    到这里,除了静态方法,我们的 Promise 实现基本已经完成了 95%

    为什么不是 100% 呢?还有一处细节没有完成,我们来看下面的代码,判断输出结果

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000);
    })
    
    promise.then(null).then(data => {
      console.log(data)
    })
    

    上面的代码会在 1s 后输出 andy

    这就是 Promise 穿透现象:给 then() 函数传递非函数值作为其参数时,实际上会被解析成 then(null) ,这时,上一个 Promise 对象的决议结果便会 “穿透” 到下一个 then 方法的参数中

    如何实现 Promise 穿透

    其实很简单,并且已经实现。在 then() 方法的实现中,我们已经为 onfulfilledonrejected 函数加上了如下判断:

    Promise.prototype.then = function (onfulfilled, onrejected) {
      onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
      onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
      
      // ...
    }
    

    如果 onfulfilled 不是函数类型,则给一个默认值,该默认值是返回其参数的函数。onreject 函数同理。这段代码就实现了 “穿透” 的逻辑

    Promise 静态方法和其他方法

    • Promise.proptype.catch
    • Promise.resolve
    • Promise.reject
    • Promise.all
    • Promise.race

    Promise.prototype.catch 实现

    Promise.prototype.catch 可以用来进行异常捕获。用法如下:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('error')
      }, 1000);
    })
    
    promise.then(data => {
      console.log(data)
    }).catch(error => {
      console.log(error)
    })
    

    上面代码会在 1s 后输出 error

    其实在这种场景下,它与以下代码是等价的

    Promise.prototype.catch = function (catchFunc) {
      return this.then(null, catchFunc)
    }
    

    因为我们知道 then() 方法的第二个参数也是进行异常捕获的,所以通过这个特性,我们可以比较简单的实现 Promise.prototype.catch

    Promise.resolve 实现

    ?:

    Promise.resolve('data').then(data => {
      console.log(data)
    })
    console.log(1)
    

    上面代码会先输出 1 ,再输出 data

    实现:

    Promise.resolve = function (value) {
      return new Promise((resolve, reject) => {
        resolve(value)
      })
    }
    

    顺带实现 Promise.reject(reason)

    Promise.reject = function (reason) {
      return new Promise((resolve, reject) => {
        reject(reason)
      })
    }
    

    Promise.all 实现

    ?:

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000)
    })
    
    
    let promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy')
      }, 1000)
    })
    
    Promise.all([promise1, promise2]).then(data => {
      console.log(data)
    })
    

    上面代码会在 1s 后输出 ["andy", "andy"]

    实现:

    Promise.all = (promiseArray) => {
      if (!Array.isArray(promiseArray)) {
        throw new TypeError('The arguments should be an array!')
      }
    
      return new Promise((resolve, reject) => {
        try {
          let resultArray = []
          const len = promiseArray.length
    
          if (len === 0) {
            return resolve(resultArray)
          }
    
          let count = 0
          for (let i = 0;i < len;i++) {
            Promise.resolve(promiseArray[i]).then(data => {
              count++
              resultArray[i] = data
    
              if (count === len) {
                resolve(resultArray)
              }
            }, reject)
          }
        } catch (e) {
          reject(e)
        }
      })
    }
    

    我们先对参数 promiseArray 的类型进行判断,如果是非数组类型参数则抛出错误。Promise.all 会返回一个 Promise 实例,这个实例将会在 promiseArray 中的所有 Promise 实例被决议后进行决议,决议结果是一个数组,这个数组存有 promiseArray 中的所有 Promise 实例的决议值

    Promise.race 实现

    ?:

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy1')
      }, 1000)
    })
    
    
    let promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('andy2')
      }, 2000)
    })
    
    Promise.race([promise1, promise2]).then(data => {
      console.log(data)
    })
    

    上面代码会在 1s 后输出 andy1

    实现:

    Promise.race = (promiseArray) => {
      if (!Array.isArray(promiseArray)) {
        throw new TypeError('The arguments should be an array!')
      }
    
      return new Promise((resolve, reject) => {
        try {
          const len = promiseArray.length
    
          if (len === 0) {
            return resolve()
          }
    
          for (let p of promiseArray) {
            Promise.resolve(p).then(resolve, reject)
          }
        } catch (e) {
          reject(e)
        }
      })
    }
    

    同步执行 promiseArray 数组中所有 Promise 实例的 then 方法,第一个 resolve 的示例会直接触发新的 Promise 实例

    总结

    Promise/A+

    1. Promise 的状态一经改变就不能再改变。(见3.1)
    2. .then.catch 都会返回一个新的 Promise
    3. catch 不管被连接到哪里,都能捕获上层的错误。(见3.2)
    4. Promise 中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如 return 2 会被包装为 return Promise.resolve(2)
    5. Promise.then 或者 .catch 可以被调用多次, 当如果 Promise 内部的状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 的时候都会直接拿到该值。(见3.5)
    6. .then 或者 .catchreturn 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。(见3.6)
    7. .then.catch 返回的值不能是 promise 本身,否则会造成死循环。(见3.7)
    8. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。(见3.8)
    9. .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为 catch.then 第二个参数的简便写法。(见3.9)
    10. .finally 方法也是返回一个 Promise ,他在 Promise 结束的时候,无论结果为 resolved 还是 rejected,都会执行里面的回调函数。
    11. .finally() 方法不管 Promise 对象最后的状态如何都会执行
    12. .finally() 方法的回调函数不接收任何参数,也就是说你在 .finally() 函数中是无法知道 Promise 的最终的状态
    13. 它最终返回的是上一次的 Promise 对象值,不过如果抛出的是一个异常则返回异常的 Promise 对象
    14. Promise.all() 的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完成后才执行回调
    15. .race() 的作用也是接收一组异步任务,然后并行执行异步任务,只保留第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃
    16. Promise.all().then() 结果中数组的顺序和 Promise.all() 接收的数组顺序一致
    17. allrace 传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被 then 的第二个参数或者后面的 catch 捕获;但并不会影响数组中其他的异步任务的执行

    起源地下载网 » 手写 Promise

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元