如何写一个让面试官满意的 Promise?

Promise 的实现没那么简单,也没想象中的那么难,200 行代码之内就能够实现一个可替代原生的 Promise。html

Promise 已是前端不可缺乏的 API,如今已是无处不在。你肯定已经很了解 Promise 吗?若是不是很了解,那么应该了解 Promise 的实现原理。若是你以为你本身挺了解的,那么你本身实现过 Promise 吗?前端

不管如何,了解 Promise 的实现方式,对于提高咱们的前端技能都有必定的帮助。jquery

下面的代码都是使用 ES6 语法实现的,不兼容 ES5,在最新的谷歌浏览器上运行没问题。git

若是你想先直接看效果,能够看文章最后的完整版,也能够看 github,github 上包括了单元测试。github

Promise 的由来

做为前端,通常最先接触 Promise 的概念是在 jquery 的1.5版本发布的 deferred objects。可是前端最先引入 P romise 的概念的却不是 jquery,而是dojo,并且 Promise 之因此叫 Promise 也是由于 dojo。Promises/A 标准的撰写者 KrisZyp 于 2009 年在 google 的 CommonJS 讨论组发了一个贴子,讨论了Promise API的设计思路。他声称想将这类 API 命名为 future,可是 dojo 已经实现的 deferred 机制中用到了 Promise这个术语,因此仍是继续使用 Promise为此机制命名。数组

更新日志

2019-10-12promise

  • 删除了 this._callbcaQueue,使用 this._nextCallback 替换浏览器

    因为链式返回的都是一个新的 Promise,因此回调其实只有一个,无需数组处理异步

部分术语

  • executor函数

    new Promise( function(resolve, reject) {...} /* executor */  );
    复制代码

    executor 是带有 resolvereject 两个参数的函数 。

  • onFulfilled

    p.then(onFulfilled, onRejected);
    复制代码

    当 Promise 变成接受状态(fulfillment)时,该参数做为 then 回调函数被调用。

  • onRejected 当Promise变成拒绝状态(rejection )时,该参数做为回调函数被调用。

    p.then(onFulfilled, onRejected);
    p.catch(onRejected);
    复制代码

Promise 实现原理

最好先看看 Promises/A+规范,这里是我的总结的代码实现的基本原理。

Promise 的三种状态

  • pending
  • fulfilled
  • rejected

Promise 对象 pending 状可能会变为 **fulfilled **和 rejected,可是不能够逆转状态。

then、catch 的回调方法只有在非 pending 状态才能执行。

Promise 生命周期

为了更好理解,本人总结了 Promise 的生命周期,生命周期分为两种状况,并且生命周期是不可逆的。

pending -> fulfilld

pending -> rejected

**executor、then、catch、finally 的执行都是有各自新的生命周期,即各自独立 Promise 环境。**链式返回的下一个 Promise 的结果来源于上一个 Promise 的结果。

链式原理

如何保持 .then.catch.finally 的链式调用呢?

其实每一个链式调用的方法返回一个新的 Promise 实例(其实这也是 Promises/A+ 规范之一,这个也是实现 Promise 的关键之处)就能够解决这个问题,同时保证了每一个链式方式的 Promise 的初始状态为 pending 状态,每一个 then、catch、finally 都有自身的 Promise 生命周期。

Promise.prototype.then = function(onFulfilled,onReject) {
  return new Promise(() => {
    // 这里处理后续的逻辑便可
  })
}
复制代码

可是须要考虑中途断链的状况,断链后继续使用链式的话,Promise 的状态多是非 pending 状态。

这一点刚接触的时候,是没那么容易搞懂的。

默认一开始就使用 new Promise(...).then(...) 进行链式调用, then、catch 等的回调函数都是处于 pending 状态,回调函数会加入异步列队等待执行。而断链的时候,可能过了几秒后才从新链式调用,那么 pending 状态就有可能变为了 fulfilled 或者 rejected 状态,须要当即执行,而不须要等待pending 状态变化后才执行。

不断链的例子以下:

new Promise(...).then(...)
复制代码

断链的例子以下:

const a = new Promise(...)
// 这中途可能过去了几秒,pending 状态就有可能变为了 fulfilled 或者 rejected 状态
a.then(...)
复制代码

因此须要考虑这两种状况。

异步列队

这个须要了解宏任务和微任务,可是,不是全部浏览器 JavaScript API 都提供微任务这一类的方法。

因此这里先使用 setTimeout 代替,主要的目的就是要等待 pending 状态切换为其余的状态(即 executor 的执行),才会执行后续链式的回调操做。

虽然异步 resolve 或者 reject 的时候,使用同步方式也能够实现。可是原生的 Promise 全部 then 等回调函数都是在异步列队中执行的,即便是同步 resolve 或者 reject。

new Promise(resolve=>{
  resolve('这是同步 resolve')
})
new Promise(resolve=>{
  setTimeout(()=>{
  	resolve('这是异步 resolve')
  })
})
复制代码

这里注意一下,后面逐步说明的例子中,前面一些代码实现是没考虑异步处理的状况的,后面涉及到异步 resolve 或者 reject 的场景才加上去的。

第一步定义好结构

这里为了跟原生的 Promise 作区别,加了个前缀,改成 NPromise。

定义状态

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected
复制代码

定义 Promise 实例方法

class NPromise {
  constructor(executor) {}
  then(onFulfilled, onRejected) {}
  catch(onRejected) {}
  finally(onFinally) {}
}
复制代码

定义拓展方法

NPromise.resolve = function(value){}
NPromise.reject = function(resaon){}
NPromise.all = function(values){}
NPromise.race = function(values){}
复制代码

简单的 Promise

第一个简单 Promsie 不考虑异步 resolve 的等状况,这一步只是用法像,then 的回调也不是异步执行的

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/** * @param {Function} executor * executor是带有 resolve 和 reject 两个参数的函数 * Promise 构造函数执行时当即调用 executor 函数 */
class NPromise {
  constructor(executor) {
    // 这里有些属性变量是能够不定义的,不过提早定义一下,提升可读性

    // 初始化状态为 pending
    this._status = PENDING
    // 传递给 then 的 onFulfilled 参数
    this._nextValue = undefined
    // 错误缘由
    this._error = undefined
    executor(this._onFulfilled, this._onRejected)
  }

  /** * 操做成功 * @param {Any} value 操做成功传递的值 */
  _onFulfilled = (value) => {
    if (this._status === PENDING) {
      this._status = FULFILLED
      this._nextValue = value
      this._error = undefined
    }
  }

  /** * 操做失败 * @param {Any} reason 操做失败传递的值 */
  _onRejected = (reason) => {
    if (this._status === PENDING) {
      this._status = REJECTED
      this._error = reason
      this._nextValue = undefined
    }
  }

  then(onFulfilled, onRejected) {
    return new NPromise((resolve, reject) => {
      if (this._status === FULFILLED) {
        if (onFulfilled) {
          const _value = onFulfilled
            ? onFulfilled(this._nextValue)
            : this._nextValue
          // 若是 onFulfilled 有定义则运行 onFulfilled 返回结果
          // 不然跳过,这里下一个 Promise 都是返回 fulfilled 状态
          resolve(_value)
        }
      }

      if (this._status === REJECTED) {
        if (onRejected) {
          resolve(onRejected(this._error))
        } else {
          // 没有直接跳过,下一个 Promise 继续返回 rejected 状态
          reject(this._error)
        }
      }
    })
  }

  catch(onRejected) {
    // catch 其实就是 then 的无 fulfilled 处理
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    // 这个后面实现
  }
}
复制代码

测试例子(setTimeout 只是为了提供独立的执行环境)

setTimeout(() => {
  new NPromise((resolve) => {
    console.log('resolved:')
    resolve(1)
  })
    .then((value) => {
      console.log(value)
      return 2
    })
    .then((value) => {
      console.log(value)
    })
})

setTimeout(() => {
  new NPromise((_, reject) => {
    console.log('rejected:')
    reject('err')
  })
    .then((value) => {
      // 这里不会运行
      console.log(value)
      return 2
    })
    .catch((err) => {
      console.log(err)
    })
})
// 输出
// resolved:
// 1
// 2
// rejected:
// err
复制代码

考虑捕获错误

仍是先不考虑异步 resolve 的等状况,这一步也只是用法像,then 的回调也不是异步执行的

相对于上一步的改动点:

  • executor 的运行须要 try catch

    then、catch、finally 都是要通过 executor 执行的,因此只须要 try catch executor 便可。

  • 没有使用 promiseInstance.catch 捕获错误则直接打印错误信息

    不是 throw,而是 console.error,原生的也是这样的,因此直接 try catch 不到 Promise 的错误。

  • then 的回调函数不是函数类型则跳过

    这不属于错误捕获处理范围,这里只是顺带提一下改动点。

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function isFunction(fn) {
  return typeof fn === 'function'
}

/** * @param {Function} executor * executor是带有 resolve 和 reject 两个参数的函数 * Promise 构造函数执行时当即调用 executor 函数 */
class NPromise {
  constructor(executor) {
    // 这里有些属性变量是能够不定义的,不过提早定义一下,提升可读性

    try {
      // 初始化状态为 pending
      this._status = PENDING
      // 传递给 then 的 onFulfilled 参数
      this._nextValue = undefined
      // 错误缘由
      this._error = undefined
      executor(this._onFulfilled, this._onRejected)
    } catch (err) {
      this._onRejected(err)
    }
  }

  /** * 若是没有 .catch 错误,则在最后抛出错误 */
  _throwErrorIfNotCatch() {
    setTimeout(() => {
      // setTimeout 是必须的,等待执行完毕,最后检测 this._error 是否还定义
      if (this._error !== undefined) {
        // 发生错误后没用 catch 那么须要直接提示
        console.error('Uncaught (in promise)', this._error)
      }
    })
  }

  /** * 操做成功 * @param {Any} value 操做成功传递的值 */
  _onFulfilled = (value) => {
    if (this._status === PENDING) {
      this._status = FULFILLED
      this._nextValue = value
      this._error = undefined
      this._throwErrorIfNotCatch()
    }
  }

  /** * 操做失败 * @param {Any} reason 操做失败传递的值 */
  _onRejected = (reason) => {
    if (this._status === PENDING) {
      this._status = REJECTED
      this._error = reason
      this._nextValue = undefined
      this._throwErrorIfNotCatch()
    }
  }

  then(onFulfilled, onRejected) {
    return new NPromise((resolve, reject) => {
      const handle = (reason) => {
        function handleResolve(value) {
          const _value = isFunction(onFulfilled) ? onFulfilled(value) : value
          resolve(_value)
        }

        function handleReject(err) {
          if (isFunction(onRejected)) {
            resolve(onRejected(err))
          } else {
            reject(err)
          }
        }

        if (this._status === FULFILLED) {
          return handleResolve(this._nextValue)
        }

        if (this._status === REJECTED) {
          return handleReject(reason)
        }
      }
      handle(this._error)
      // error 已经传递到下一个 NPromise 了,须要重置,不然会打印多个相同错误
      // 配合 this._throwErrorIfNotCatch 一块儿使用,
      // 保证执行到最后才抛出错误,若是没有 catch
      this._error = undefined
    })
  }

  catch(onRejected) {
    // catch 其实就是 then 的无 fulfilled 处理
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    // 这个后面实现
  }
}
复制代码

测试例子(setTimeout 只是为了提供独立的执行环境)

setTimeout(() => {
  new NPromise((resolve) => {
    console.log('executor 报错:')
    const a = 2
    a = 3
    resolve(1)
  }).catch((value) => {
    console.log(value)
    return 2
  })
})

setTimeout(() => {
  new NPromise((resolve) => {
    resolve()
  })
    .then(() => {
      const b = 3
      b = 4
      return 2
    })
    .catch((err) => {
      console.log('then 回调函数报错:')
      console.log(err)
    })
})

setTimeout(() => {
  new NPromise((resolve) => {
    console.log('直接打印了错误信息,红色的:')
    resolve()
  }).then(() => {
    throw Error('test')
    return 2
  })
})


// executor 报错:
// TypeError: Assignment to constant variable.
// at <anonymous>:97:7
// at new NPromise (<anonymous>:21:7)
// at <anonymous>:94:3
// then 回调函数报错: TypeError: Assignment to constant variable.
// at <anonymous>:111:9
// at <anonymous>:59:17
// at new Promise (<anonymous>)
// at NPromise.then (<anonymous>:54:12)
// at <anonymous>:109:6
// 直接打印了错误信息,红色的:
// Uncaught (in promise) Error: test
// at <anonymous>:148:11
// at handleResolve (<anonymous>:76:52)
// at handle (<anonymous>:89:18)
// at <anonymous>:96:7
// at new NPromise (<anonymous>:25:7)
// at NPromise.then (<anonymous>:73:12)
// at <anonymous>:147:6
复制代码

考虑 resolved 的值是 Promise 类型

仍是先不考虑异步 resolve 的等状况,这一步也只是用法像,then 的回调也不是异步执行的

相对于上一步的改动点:

  • 新增 isPromise 判断方法
  • then 方法中处理 this._nextValue 是 Promise 的状况

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function isFunction(fn) {
  return typeof fn === 'function'
}

function isPromise(value) {
  return (
    value &&
    isFunction(value.then) &&
    isFunction(value.catch) &&
    isFunction(value.finally)
    // 上面判断也能够用一句代码代替,不过原生的试了下,不是用下面的方式
    // value instanceof NPromise
  )
}

/** * @param {Function} executor * executor是带有 resolve 和 reject 两个参数的函数 * Promise 构造函数执行时当即调用 executor 函数 */
class NPromise {
  constructor(executor) {
    // 这里有些属性变量是能够不定义的,不过提早定义一下,提升可读性

    try {
      // 初始化状态为 pending
      this._status = PENDING
      // 传递给 then 的 onFulfilled 参数
      this._nextValue = undefined
      // 错误缘由
      this._error = undefined
      executor(this._onFulfilled, this._onRejected)
    } catch (err) {
      this._onRejected(err)
    }
  }

  /** * 若是没有 .catch 错误,则在最后抛出错误 */
  _throwErrorIfNotCatch() {
    setTimeout(() => {
      // setTimeout 是必须的,等待执行完毕,最后检测 this._error 是否还定义
      if (this._error !== undefined) {
        // 发生错误后没用 catch 那么须要直接提示
        console.error('Uncaught (in promise)', this._error)
      }
    })
  }

  /** * 操做成功 * @param {Any} value 操做成功传递的值 */
  _onFulfilled = (value) => {
    if (this._status === PENDING) {
      this._status = FULFILLED
      this._nextValue = value
      this._error = undefined
      this._throwErrorIfNotCatch()
    }
  }

  /** * 操做失败 * @param {Any} reason 操做失败传递的值 */
  _onRejected = (reason) => {
    if (this._status === PENDING) {
      this._status = REJECTED
      this._error = reason
      this._nextValue = undefined
      this._throwErrorIfNotCatch()
    }
  }

  then(onFulfilled, onRejected) {
    return new NPromise((resolve, reject) => {
      const handle = (reason) => {
        function handleResolve(value) {
          const _value = isFunction(onFulfilled) ? onFulfilled(value) : value
          resolve(_value)
        }

        function handleReject(err) {
          if (isFunction(onRejected)) {
            resolve(onRejected(err))
          } else {
            reject(err)
          }
        }

        if (this._status === FULFILLED) {
          if (isPromise(this._nextValue)) {
            return this._nextValue.then(handleResolve, handleReject)
          } else {
            return handleResolve(this._nextValue)
          }
        }

        if (this._status === REJECTED) {
          return handleReject(reason)
        }
      }
      handle(this._error)
      // error 已经传递到下一个 NPromise 了,须要重置,不然会打印多个相同错误
      // 配合 this._throwErrorIfNotCatch 一块儿使用,
      // 保证执行到最后才抛出错误,若是没有 catch
      this._error = undefined
    })
  }

  catch(onRejected) {
    // catch 其实就是 then 的无 fulfilled 处理
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    // 这个后面实现
  }
}
复制代码

测试代码

setTimeout(() => {
  new NPromise((resolve) => {
    resolve(
      new NPromise((_resolve) => {
        _resolve(1)
      })
    )
  }).then((value) => {
    console.log(value)
  })
})

setTimeout(() => {
  new NPromise((resolve) => {
    resolve(
      new NPromise((_, _reject) => {
        _reject('err')
      })
    )
  }).catch((err) => {
    console.log(err)
  })
})
复制代码

考虑异步的状况

异步 resolve 或者 reject,相对会复杂点,回调须要等待 pending 状态变为其余状态后才执行。

原生 JavaScript 自带异步列队,咱们能够利用这一点,这里先使用 setTimeout 代替,因此这个 Promise 的优先级跟 setTimeout 是同一个等级的(原生的是 Promise 优先于 setTimeout 执行)。

相对于上一步的改动点:

  • this._onFulfilled 加上了异步处理,即加入所谓的异步列队

  • this._onRejected 加上了异步处理,即加入所谓的异步列队

  • 新增 this._nextCallback

    因为链式返回的都是一个新的 Promise,因此下一步的回调其实只有一个,只有 Promise 的状态为 pending 的时候,this._nextCallback 才会有值(非 pending 的时候回调已经当即执行),同时等待 pending 状态改变后才会执行。

    this._nextCallback 都只会在当前 Promise 生命周期中执行一次。

  • then 方法中须要根据 Promise 状态进行区分处理

    若是非 pending 状态,那么当即执行回调函数(若是没回调函数,跳过)。

    若是是 pending 状态 ,那么加入异步列队(经过 this._nextCallback 传递回调),等待 Promise 状态为非 pending 状态才执行。

  • then 方法的回调加上了 try catch 捕获错误处理

  • 实现了 finally 方法

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function isFunction(fn) {
  return typeof fn === 'function'
}

function isPromise(value) {
  return (
    value &&
    isFunction(value.then) &&
    isFunction(value.catch) &&
    isFunction(value.finally)
    // 上面判断也能够用一句代码代替,不过原生的试了下,不是用下面的方式
    // value instanceof NPromise
  )
}

/** * @param {Function} executor * executor是带有 resolve 和 reject 两个参数的函数 * Promise 构造函数执行时当即调用 executor 函数 */
class NPromise {
  constructor(executor) {
    if (!isFunction(executor)) {
      throw new TypeError('Expected the executor to be a function.')
    }
    try {
      // 初始化状态为 PENDING
      this._status = PENDING
      // fullfilled 的值,也是 then 和 catch 的 return 值,都是当前执行的临时值
      this._nextValue = undefined
      // 当前捕捉的错误信息
      this._error = undefined
      // then、catch、finally 的异步回调列队,会依次执行
      // 改动点:因为 then、catch 都是返回新的 Promise 因此无需列队执行,只须要 nextCallback
      // this._callbacQueue = []
      // 异步 resolve 或者 reject 的处理须要等待 pending 状态转变后才执行下一步的回调
      this._nextCallback = undefined
      executor(this._onFulfilled, this._onRejected)
    } catch (err) {
      this._onRejected(err)
    }
  }

  /** * 若是没有 .catch 错误,则在最后抛出错误 */
  _throwErrorIfNotCatch() {
    setTimeout(() => {
      // setTimeout 是必须的,等待执行完毕,最后检测 this._error 是否还定义
      if (this._error !== undefined) {
        // 发生错误后没用 catch 那么须要直接提示
        console.error('Uncaught (in promise)', this._error)
      }
    })
  }

  /** * 操做成功 * @param {Any} value 操做成功传递的值 */
  _onFulfilled = value => {
    setTimeout(() => {
      if (this._status === PENDING) {
        this._status = FULFILLED
        this._nextValue = value
        this._error = undefined
        this._nextCallback && this._nextCallback()
        this._throwErrorIfNotCatch()
      }
    })
  }

  /** * 操做失败 * @param {Any} reason 操做失败传递的值 */
  _onRejected = reason => {
    setTimeout(() => {
      if (this._status === PENDING) {
        this._status = REJECTED
        this._error = reason
        this._nextValue = undefined
        this._nextCallback && this._nextCallback()
        this._throwErrorIfNotCatch()
      }
    })
  }

  then(onFulfilled, onRejected) {
    return new NPromise((resolve, reject) => {
      const handle = reason => {
        try {
          function handleResolve(value) {
            const _value = isFunction(onFulfilled) ? onFulfilled(value) : value
            resolve(_value)
          }

          function handleReject(err) {
            if (isFunction(onRejected)) {
              resolve(onRejected(err))
            } else {
              reject(err)
            }
          }

          if (this._status === FULFILLED) {
            if (isPromise(this._nextValue)) {
              return this._nextValue.then(handleResolve, handleReject)
            } else {
              return handleResolve(this._nextValue)
            }
          }

          if (this._status === REJECTED) {
            return handleReject(reason)
          }
        } catch (err) {
          reject(err)
        }
      }

      const nextCallback = () => {
        handle(this._error)
        // error 已经传递到下一个 NPromise 了,须要重置,不然会抛出多个相同错误
        // 配合 this._throwErrorIfNotCatch 一块儿使用,
        // 保证执行到最后才抛出错误,若是没有 catch
        this._error = undefined
      }

      if (this._status === PENDING) {
        // 默认不断链的状况下,then 回电函数 Promise 的状态为 pending 状态(必定的)
        // 如 new NPromise(...).then(...) 是没断链的
        // 下面是断链的
        // var a = NPromise(...); a.then(...)
        // 固然断链了也多是 pending 状态
        this._nextCallback = nextCallback
      } else {
        // 断链的状况下,then 回调函数 Promise 的状态为非 pending 状态的场景下
        // 如 var a = NPromise(...);
        // 过了几秒后 a.then(...) 继续链式调用就多是非 pending 的状态了
        nextCallback() // 须要当即执行
      }
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    return this.then(
      () => {
        onFinally()
        return this._nextValue
      },
      () => {
        onFinally()
        // 错误须要抛出,下一个 Promise 才会捕获到
        throw this._error
      }
    )
  }
}
复制代码

测试代码

new NPromise((resolve) => {
  setTimeout(() => {
    resolve(1)
  }, 1000)
})
  .then((value) => {
    console.log(value)
    return new NPromise((resolve) => {
      setTimeout(() => {
        resolve(2)
      }, 1000)
    })
  })
  .then((value) => {
    console.log(value)
  })
复制代码

拓展方法

拓展方法相对难的应该是 Promise.all,其余的都挺简单的。

NPromise.resolve = function(value) {
  return new NPromise(resolve => {
    resolve(value)
  })
}

NPromise.reject = function(reason) {
  return new NPromise((_, reject) => {
    reject(reason)
  })
}

NPromise.all = function(values) {
  return new NPromise((resolve, reject) => {
    let ret = {}
    let isError = false
    values.forEach((p, index) => {
      if (isError) {
        return
      }
      NPromise.resolve(p)
        .then(value => {
          ret[index] = value
          const result = Object.values(ret)
          if (values.length === result.length) {
            resolve(result)
          }
        })
        .catch(err => {
          isError = true
          reject(err)
        })
    })
  })
}

NPromise.race = function(values) {
  return new NPromise(function(resolve, reject) {
    values.forEach(function(value) {
      NPromise.resolve(value).then(resolve, reject)
    })
  })
}
复制代码

最终版

也能够看 github,上面有单元测试。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function isFunction(fn) {
  return typeof fn === 'function'
}

function isPromise(value) {
  return (
    value &&
    isFunction(value.then) &&
    isFunction(value.catch) &&
    isFunction(value.finally)
    // 上面判断也能够用一句代码代替,不过原生的试了下,不是用下面的方式
    // value instanceof NPromise
  )
}

/** * @param {Function} executor * executor是带有 resolve 和 reject 两个参数的函数 * Promise 构造函数执行时当即调用 executor 函数 */
class NPromise {
  constructor(executor) {
    if (!isFunction(executor)) {
      throw new TypeError('Expected the executor to be a function.')
    }
    try {
      // 初始化状态为 PENDING
      this._status = PENDING
      // fullfilled 的值,也是 then 和 catch 的 return 值,都是当前执行的临时值
      this._nextValue = undefined
      // 当前捕捉的错误信息
      this._error = undefined
      // then、catch、finally 的异步回调列队,会依次执行
      // 改动点:因为 then、catch 都是返回新的 Promise 因此无需列队执行,只须要 nextCallback
      // this._callbacQueue = []
      // 异步 resolve 或者 reject 的处理须要等待 pending 状态转变后才执行下一步的回调
      this._nextCallback = undefined
      executor(this._onFulfilled, this._onRejected)
    } catch (err) {
      this._onRejected(err)
    }
  }

  /** * 若是没有 .catch 错误,则在最后抛出错误 */
  _throwErrorIfNotCatch() {
    setTimeout(() => {
      // setTimeout 是必须的,等待执行完毕,最后检测 this._error 是否还定义
      if (this._error !== undefined) {
        // 发生错误后没用 catch 那么须要直接提示
        console.error('Uncaught (in promise)', this._error)
      }
    })
  }

  /** * 操做成功 * @param {Any} value 操做成功传递的值 */
  _onFulfilled = value => {
    setTimeout(() => {
      if (this._status === PENDING) {
        this._status = FULFILLED
        this._nextValue = value
        this._error = undefined
        this._nextCallback && this._nextCallback()
        this._throwErrorIfNotCatch()
      }
    })
  }

  /** * 操做失败 * @param {Any} reason 操做失败传递的值 */
  _onRejected = reason => {
    setTimeout(() => {
      if (this._status === PENDING) {
        this._status = REJECTED
        this._error = reason
        this._nextValue = undefined
        this._nextCallback && this._nextCallback()
        this._throwErrorIfNotCatch()
      }
    })
  }

  then(onFulfilled, onRejected) {
    return new NPromise((resolve, reject) => {
      const handle = reason => {
        try {
          function handleResolve(value) {
            const _value = isFunction(onFulfilled) ? onFulfilled(value) : value
            resolve(_value)
          }

          function handleReject(err) {
            if (isFunction(onRejected)) {
              resolve(onRejected(err))
            } else {
              reject(err)
            }
          }

          if (this._status === FULFILLED) {
            if (isPromise(this._nextValue)) {
              return this._nextValue.then(handleResolve, handleReject)
            } else {
              return handleResolve(this._nextValue)
            }
          }

          if (this._status === REJECTED) {
            return handleReject(reason)
          }
        } catch (err) {
          reject(err)
        }
      }

      const nextCallback = () => {
        handle(this._error)
        // error 已经传递到下一个 NPromise 了,须要重置,不然会抛出多个相同错误
        // 配合 this._throwErrorIfNotCatch 一块儿使用,
        // 保证执行到最后才抛出错误,若是没有 catch
        this._error = undefined
      }

      if (this._status === PENDING) {
        // 默认不断链的状况下,then 回电函数 Promise 的状态为 pending 状态(必定的)
        // 如 new NPromise(...).then(...) 是没断链的
        // 下面是断链的
        // var a = NPromise(...); a.then(...)
        // 固然断链了也多是 pending 状态
        this._nextCallback = nextCallback
      } else {
        // 断链的状况下,then 回调函数 Promise 的状态为非 pending 状态的场景下
        // 如 var a = NPromise(...);
        // 过了几秒后 a.then(...) 继续链式调用就多是非 pending 的状态了
        nextCallback() // 须要当即执行
      }
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    return this.then(
      () => {
        onFinally()
        return this._nextValue
      },
      () => {
        onFinally()
        // 错误须要抛出,下一个 Promise 才会捕获到
        throw this._error
      }
    )
  }
}

NPromise.resolve = function(value) {
  return new NPromise(resolve => {
    resolve(value)
  })
}

NPromise.reject = function(reason) {
  return new NPromise((_, reject) => {
    reject(reason)
  })
}

NPromise.all = function(values) {
  return new NPromise((resolve, reject) => {
    let ret = {}
    let isError = false
    values.forEach((p, index) => {
      if (isError) {
        return
      }
      NPromise.resolve(p)
        .then(value => {
          ret[index] = value
          const result = Object.values(ret)
          if (values.length === result.length) {
            resolve(result)
          }
        })
        .catch(err => {
          isError = true
          reject(err)
        })
    })
  })
}

NPromise.race = function(values) {
  return new NPromise(function(resolve, reject) {
    values.forEach(function(value) {
      NPromise.resolve(value).then(resolve, reject)
    })
  })
}
复制代码

总结

实现 Promise 比较关键的点在于状态的切换,而后链式的处理(返回新的 Promise,核心点)。最主要的逻辑仍是 then 方法的处理(核心),理解了 then 方法里面的逻辑,那么就了解了大部分了。

通过一些测试,除了下面两点以外:

  • 使用了 setTimeout 的宏任务列队外替代微任务
  • 拓展方法 Promise.all 和 Promise.race 只考虑数组,不考虑迭代器。

NPromise 单独用法和效果上基本 100% 跟原生的一致。

若是你不相信,看看 github 上的单元测试,同时你试试下面的代码:

注意 NPromise 和 Promise 的。

new NPromise((resolve) => {
  resolve(Promise.resolve(2))
}).then((value) => {
  console.log(value)
})
复制代码

或者

new Promise((resolve) => {
  resolve(NPromise.resolve(2))
}).then((value) => {
  console.log(value)
})
复制代码

上面的结果都是返回正常的。

参考文章