Skip to content
大纲

手写 Promise

基本概念

Promise 本质是一个状态机,且状态只能为以下三种:

  1. Pending(等待态)
  2. Fulfilled(执行态)
  3. Rejected(拒绝态)

状态的变更是单向的,Pending 到下一状态,且状态变更不可逆

js
Pending ==> Fulfilled
Pending ==> Rejected

Promise 的基本过程

  1. 初始化 Promise 状态为 pending
  2. 立即执行 Promise 中传入的 fn 函数,将内部函数 resolve 和 reject 作为参数传递给 fn,
  3. 执行 .then() 注册回调处理数组
  4. then 方法传入的参数 onFulfilled 和 onRejected 参数不是函数是的处理,与是函数时必须在那一轮事件循环之后的新执行栈中执行

所有方法

手写所有方法

  • Promise 的含义
  • Promise.prototype.then() ES2015
  • Promise.prototype.catch() ES2015
  • Promise.resolve() ES2015
  • Promise.reject() ES2015
  • Promise.all() ES2015
  • Promise.race() ES2015
  • Promise.prototype.finally() ES2018
  • Promise.allSettled() ES2020
  • Promise.any() ES2021
  • Promise.try() Stage 1
  • await.ops ESnext

具体文档相关内容,参看 learn-javascript es6 Promise

几个问题

手写实现

参照 Promise A+ 的规范一步一步实现 my-promise 1~8.js

js
// 结构

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

class MyPromise {
  constructor() {}

  status = PENDING
  value = null
  reason = null
  onFulfilledCallbacks = []
  onRejectedCallbacks = []

  then() {}
  catch() {}
  finally() {}

  static all() {}
  static race() {}
  static resolve() {}
  static reject() {}
  static allSettled() {}
  static any() {}
  static try() {}
}
js
// 参考学习:https://juejin.cn/post/6945319439772434469

{
  // 1. 传入执行器 executor
  class MyPromise1 {
    constructor(executor) {
      // executor 是一个执行器,进入会立即执行
      executor()
    }
  }
}

{
  // 2. executor 传入 resolve 和 reject 方法
  class MyPromise2 {
    constructor(executor) {
      // executor 是一个执行器,进入会立即执行
      // 并传入 resolve 和 reject 方法
      executor(this.resolve, this.reject)
    }

    // resolve 和 reject 为什么要用箭头函数?
    // 如果直接调用的话,普通函数 this 指向的是 window 或者 undefined
    // 用箭头函数就可以让 this 指向当前实例对象
    // 更改成功后的状态
    resolve = () => {}
    // 更改失败后的状态
    reject = () => {}
  }
}

// 3. 状态与结果的管理

// 定义三个常量表示状态
{
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'

  class MyPromise3 {
    constructor(executor) {
      executor(this.resolve, this.reject)
    }

    // 储存状态的变量,初始值是 pending
    status = PENDING
    // 成功之后的值
    value = null
    // 失败的原因
    reason = null

    resolve = (value) => {
      // 只有状态是等待,才执行状态修改
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
      }
    }

    reject = (reason) => {
      // 只有状态是等待,才执行状态修改
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
      }
    }
  }
}

// 4. then 的简单实现
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject)
  }

  status = PENDING

  value = null

  reason = null

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
    }
  }

  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
    }
  }

  then(onFulfilled, onRejected) {
    // 判断状态
    if (this.status === FULFILLED) {
      // 调用成功回调,并且把值返回
      onFulfilled(this.value)
    } else if (this.status === REJECTED) {
      // 调用失败回调,并且把原因返回
      onRejected(this.reason)
    } else if (this.status === PENDING) {
      // 未处理 pending
      console.log('pending')
    }
  }
}

// 对外暴露 MyPromise 类
// export default MyPromise

// testing
const p1 = new MyPromise((resolve, reject) => {
  resolve('success')
  reject('err')
})

p1.then(
  (value) => {
    console.log('p1 resolve', value)
  },
  (reason) => {
    console.log('p1 reject', reason)
  }
)

// 当前问题,未处理异步逻辑,
// then 会马上执行,虽然判断了 pending 状态,但没有等待这个状态
// 接下来还需要处理一下 pending 的状态,参见 2.js
const p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 2000)
})

p2.then(
  (value) => {
    console.log('p2 resolve', value)
  },
  (reason) => {
    console.log('p2 reject', reason)
  }
)
js
// 接 1.js 接下来处理 pending 状态
// 先将 1.js 的实现手写迁移过来

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

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject)
  }
  status = PENDING
  value = null
  reason = null

  // 存储成功或失败的回调函数
  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value

      // 状态变更为成功,调用所有成功的回调
      while (this.onFulfilledCallback.length) {
        // Array.shift() 取出数组第一个元素,然后()调用,shift 不是纯函数,取出后会改变数组,直到数组为空
        // 这里清空了数组,实际并不必要,for 循环也是也可以的
        // 思考 如果执行的过程出错了,怎么办
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (value) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason

      // 状态变更为失败,调用所有失败的回调
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilledCallback(this.value)
    } else if (this.status === REJECTED) {
      onRejectedCallback(this.reason)
    } else if (this.status === PENDING) {
      // 不知道后续的状态变化,先将回调存储起来,
      // 等到状态变更时调用
      this.onFulfilledCallback.push(onFulfilled)
      this.onRejectedCallback.push(onRejected)
    }
  }
}

// testing pending
const p1 = new MyPromise((resolve, reject) => {
  // resolve('success')

  setTimeout(() => {
    resolve('success')
    reject('fail')
  }, 2000)
})

p1.then(
  (value) => {
    console.log('0')
    console.log('resolve', value)
  },
  (reason) => {
    console.log('reject', reason)
  },
)

// 回调存储使用数组,支持 then 方法多次调用
p1.then(
  (value) => {
    console.log('1')
    console.log('resolve1', value)
  },
  (reason) => {
    console.log('reject1', reason)
  },
)
p1.then(
  (value) => {
    console.log('2')
    console.log('resolve2', value)
  },
  (reason) => {
    console.log('reject2', reason)
  },
)

// 此时还不支持 then 的链式调用,参见 3.js
js
// 上接 2.js,下面处理 then 的链式调用
// 先手写 2.js 中已实现的功能

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

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject)
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }
  // then 链式调用,就要返回一个 Promise 对象
  // then 方法里 return 的值作为下一个 then 方法的参数,如果 return 的是一个 promise,就需要判断这个它的状态
  then(onFulfilled, onRejected) {
    const promise = new MyPromise((resolve, reject) => {
      // 这里的内容在执行器中,会立即执行(此时返回为 pending 状态)
      if (this.status === FULFILLED) {
        // 将成功回调的结果,传入 resolvePromise 集中处理(这里仅处理了 FULFILLED 状态的逻辑)
        const x = onFulfilled(this.value)
        resolvePromise(x, resolve, reject)
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      } else if (this.status === PENDING) {
        console.log('pending')
        this.onFulfilledCallback.push(onFulfilled)
        this.onRejectedCallback.push(onRejected)
      }
    })
    return promise
  }
}

function resolvePromise(x, resolve, reject) {
  // 判断 x 是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    // 是 Promise, 则执行 x, 调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason)) 简化如下
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

// testing
const p1 = new MyPromise((resolve, reject) => {
  // resolve('success')
  // reject('fail')

  // TODO: 这里当使用异步时,链式调用变得失效了 (此问题待后续处理)
  // 可以先思考下这里为什么会导致链式调用失效
  setTimeout(() => {
    resolve('success')
    reject('fail')
  }, 2000)
})

// 链式调用
p1.then((res) => {
  console.log('1', res)
  return 'then1'
}).then((res) => {
  console.log('then2', res)
})

// 目前我们没有做异常处理,接下来在 4.js 中我们异常捕获等情况

// NOTE: 思考,这里异步为什么会导致链式调用失效

// NOTE: 解析在下方,你确定要看,不先自行思考下

// 因为 pending 时,存储的回调不具备改变当前 promise 的能力(没有 resolve,reject 的相关处理),
// 所以此时第一个 then 返回的 promise 一直是 pending 状态(不是链式调用失效了,而是一直 pending 还未调用后面的链上 then 方法)
js
// 续 3.js,这里处理一下错误类型处理,以及异常捕获(执行器异常,then 执行异常)
// 先手写 3.js 流程,后补充错误处理

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

class MyPromise {
  constructor(executor) {
    // 处理执行器异常捕获
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        queueMicrotask(() => {
          // 处理 then 执行异常捕获
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onFulfilled)
        this.onRejectedCallback.push(onRejected)
      }
    })
    return promise
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

// testing
// const p0 = new MyPromise((resolve, reject) => {
//   resolve('resolve')
//   reject('reject')

//   setTimeout(() => {
//     resolve('resolve')
//     reject('reject')
//   }, 2000)
// })

// 链式调用
// p0.then((res) => {
//   console.log('1', res);
//   return 'then1'
// }).then((res) => {
//   console.log('then2', res)
// })
// p0.then((res) => {
//   console.log('2', res);
//   return 'then2'
// }).then((res) => {
//   console.log('then3', res)
// })

// 处理错误

// 1. 处理 执行器错误捕获
// const p1 = new MyPromise((resolve, reject) => {
//   // new Error('执行器错误')
//   throw new Error('执行器错误')
// }).then(res => {
//   console.log('p1 success', res)
// }, err => {
//   console.log('p1 err', err.message)
// })

// 2. 处理 then 方法执行错误捕获
// const p2 = new MyPromise((resolve, reject) => {
//   // new Error('执行器错误')
//   // throw new Error('执行器错误')
//   resolve('resolve')
//   reject('reject')

//   // 这里异步时,未良好处理链式调用,可先跳过
// })

// p2.then(res => {
//   // 第一个 then 方法中的错误要在第二个 then 方法中捕获到
//   console.log('p2-1 success', res)
//   throw new Error('then 方法执行错误')
// }, err => {
//   console.log('p2-1 err', err.message)
// }).then(res => {
//   console.log('p2-2 success', res)
// }, err => {
//   console.log('p2-2 err', err.message)
// })

// 3. then 链式调用自身,导致循环调用问题
// 如果 then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错
// 原生 Promise 会报类型错误
// const promise = new Promise((resolve, reject) => {
//   resolve('success')
//   reject('fail')
// })
// TypeError: Chaining cycle detected for promise #<Promise>
//  Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
// const p31 = promise.then(res => {
//   return p31
// })

// p31.then(res => {
//   console.log(res)
// }).catch(err => {
//   // Chaining cycle detected for promise #<Promise>
//   console.log(err.message)
// })

// 我们这里报
// ReferenceError: Cannot access 'p2' before initialization
// SyntaxError: Identifier 'promise' has already been declared
const promise = new MyPromise((resolve, reject) => {
  resolve('success')
  reject('fail')
})

// ReferenceError: Cannot access 'p32' before initialization
// 处理这个错误,需要创建一个异步函数去等待 其 (p32) 完成初始化 (可通过 queueMicrotask 实现)
const p32 = promise.then((res) => {
  console.log('2', res)
  return p32
})
p32.then(
  (res) => {
    console.log('res', res)
  },
  (err) => {
    console.log('err', err)
  }
)

// 以上三个异常情况处理后,我们 fulfilled 分支下的流程算是处理,接下来还要把 rejected 和 pending 条件分支的逻辑都处理掉
// 参见 5.js
js
// 接 4.js, 4 中我们处理了 fulfilled 状态下的错误流程,下面来处理下
//  - 将 rejected 和 pending 状态的也改造下
//  - then 中的 onRejected 回调可以不传

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

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    // 参数判断
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    const promise = new MyPromise((resolve, reject) => {
      // 提取方法,以复用错误
      const onResolve = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }
      const onReject = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }

      if (this.status === FULFILLED) {
        onResolve()
      } else if (this.status === REJECTED) {
        onReject()
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onResolve)
        this.onRejectedCallback.push(onReject)
      }
    })
    return promise
  }
}

function resolvePromise(p, x, resolve, reject) {
  if (p === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>'),
    )
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

// testing
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve')
    reject('reject')
  }, 2000)
})

const p1 = promise.then((res) => {
  console.log('1', res)
  return 'p1'
})

p1.then((res) => {
  console.log('2', res)
  return '2'
}).then((res) => {
  console.log('3', res)
})
js
// 续 5.js, 到这里核心的 Promise 功能就完成了,
// 但还缺少静态方法 resolve、reject
// 这里添加静态方法 Promise.resolve, Promise.reject

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

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    // 解决 onFufilled,onRejected 没有传值的问题
    const onFulfilled1 =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    // 因为错误的值要让后面访问到,所以这里也要抛出错误,不然会在之后 then 的 resolve 中捕获
    const onRejected1 =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    const promise2 = new MyPromise((resolve, reject) => {
      const onResolve = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled1(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }
      const onReject = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected1(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }

      if (this.status === FULFILLED) {
        onResolve()
      } else if (this.status === REJECTED) {
        onReject()
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onResolve)
        this.onRejectedCallback.push(onReject)
      }
    })
    return promise2
  }

  // 静态方法 resolve
  static resolve(parameter) {
    // 如果传入 promise 直接返回
    if (parameter instanceof MyPromise) {
      return parameter
    }
    // 转为 promise
    return new MyPromise((resolve) => {
      resolve(parameter)
    })
  }
  // 静态方法 reject
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
}

function resolvePromise(p, x, resolve, reject) {
  if (p === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }

  // 此处不符合 2.3.3 规范
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

// 基本完成了
// testing
// const p1 = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('resolve')
//   }, 2000)
// })
// const p2 = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     reject('reject')
//   }, 2000)
// })
// MyPromise.resolve().then(res => {
//   console.log(res)
// })
// MyPromise.resolve(p1).then(res => {
//   console.log('res', res)
// }, err => {
//   console.log('err', err)
// })
// MyPromise.resolve(p2).then(res => {
//   console.log('res', res)
// }, err => {
//   console.log('err', err)
// })

// MyPromise.reject('static reject').then(null, err => {
//   console.log(err);
// })

// 接下来我们就可以跑下 Promise A+ 单元测试了
//   npm i promises-aplus-tests -D
//   promises-aplus-tests MyPromise

// https://promisesaplus.com/

// 单测需要附加以下代码
MyPromise.deferred = function () {
  var result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = MyPromise

/*

6.js 单测从 2.3.3 用例开始报错,这里对比 Promise A+ 规范

```bash
2.3.3: Otherwise, if `x` is an object or function,
    2.3.3.1: Let `then` be `x.then`
      `x` is an object with null prototype
        1) via return from a fulfilled promise
        2) via return from a rejected promise
      `x` is an object with normal Object.prototype
        3) via return from a fulfilled promise
        4) via return from a rejected promise
      `x` is a function
        5) via return from a fulfilled promise
        6) via return from a rejected promise
    2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with `e` as the reason.
      `e` is `undefined`
        7) via return from a fulfilled promise
        8) via return from a rejected promise
      `e` is `null`
        9) via return from a fulfilled promise
        10) via return from a rejected promise
```

规划 2.3.3 中,要求判断 x.then 是否存在,与前面写的 `x instanceof MyPromise` 类似,但更严格,接下来对这块进行改造

根据这个规范要求,改造一下 resolvePromise 方法

*/
js
// 续 6.js, 处理 resolvePromise 方法中对于规范 2.3.3 ~ 2.3.4 的实施
// https://promisesaplus.com/

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

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    const onFulfilled1 =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    const onRejected1 =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    const promise2 = new MyPromise((resolve, reject) => {
      const onResolve = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled1(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }
      const onReject = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected1(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }

      if (this.status === FULFILLED) {
        onResolve()
      } else if (this.status === REJECTED) {
        onReject()
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onResolve)
        this.onRejectedCallback.push(onReject)
      }
    })
    return promise2
  }

  static resolve(parameter) {
    if (parameter instanceof MyPromise) {
      return parameter
    }
    return new MyPromise((resolve) => {
      resolve(parameter)
    })
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
}

function resolvePromise(p, x, resolve, reject) {
  if (p === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }

  // 按规范 2.3.3 ~ 2.3.4 对以下逻辑进行处理
  // if (x instanceof MyPromise) {
  //   x.then(resolve, reject)
  // } else {
  //   resolve(x)
  // }

  // 修改如下
  if (typeof x === 'object' || typeof x === 'function') {
    if (x === null) return resolve(x)

    let then
    try {
      then = x.then
    } catch (err) {
      // 如果取 x.then 的值时抛出错误 err,则以 err 为据因拒绝 promise
      return reject(err)
    }

    // 如果 then 是函数 (应该作为函数,这也是 thenable 的条件)
    if (typeof then === 'function') {
      let called = false
      try {
        // then 只能被调用一次,状态变更后不可再调用
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(p, y, resolve, reject)
          },
          (r) => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } catch (err) {
        if (called) return
        reject(err)
      }
    } else {
      // 如果 then 不是函数,则以 x 为参数执行 premise
      resolve(x)
    }
  } else {
    // 如果 then 不是对象或函数,则以 x 为参数执行 premise
    resolve(x)
  }
}

// 单测需要附加以下代码
MyPromise.deferred = function () {
  var result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = MyPromise

// 完成后,可以通过整个 Promise A+ 的单元测试了
js
// 现在 7.js 所有单测都通过了,但是还缺少一些方法
// catch
// finally ES2018
// all
// race
// allSettled ES2020
// any ES2021
// try 提案阶段

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

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  onFulfilledCallback = []
  onRejectedCallback = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    const onFulfilled1 =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    const onRejected1 =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    const promise2 = new MyPromise((resolve, reject) => {
      const onResolve = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled1(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }
      const onReject = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected1(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        })
      }

      if (this.status === FULFILLED) {
        onResolve()
      } else if (this.status === REJECTED) {
        onReject()
      } else if (this.status === PENDING) {
        this.onFulfilledCallback.push(onResolve)
        this.onRejectedCallback.push(onReject)
      }
    })
    return promise2
  }
  // 只对 reject 做处理
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
  // ES2018
  finally(fn) {
    return this.then(
      (value) => {
        return MyPromise.resolve(fn()).then(() => value)
      },
      (err) => {
        return MyPromise.resolve(fn()).then(() => {
          throw err
        })
      }
    )
  }

  static resolve(parameter) {
    if (parameter instanceof MyPromise) {
      return parameter
    }
    return new MyPromise((resolve) => {
      resolve(parameter)
    })
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  // 1. 传入空的可迭代对象 返回已完成 resolved
  // 2. 传入的参数不包含 promise,返回异步完成 resolved
  // 3. 所有都都成功或任何一个失败,就变为 resolved 或 reject
  static all(promiseList = []) {
    return new MyPromise((resolve, reject) => {
      const result = []
      const length = promiseList.length
      let count = 0

      if (length === 0) {
        return resolve(result)
      }

      promiseList.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (value) => {
            count++
            result[index] = value
            if (count === length) {
              resolve[result]
            }
          },
          (reason) => {
            reject(reason)
          }
        )
      })
    })
  }
  // 1. 传入空的可迭代对象,则返回的 promise 将永远等待
  // 2. 返回第一个结果值(无论 resolved 或 rejected)
  static race(promiseList = []) {
    return new MyPromise((resolve, reject) => {
      const length = promiseList.length

      if (length === 0) {
        return // 永远 pending 中
        // 思考 这种永远 pending 会有什么影响
        // return resolve()
      }
      for (let i = 0; i < length; i++) {
        MyPromise.resolve(promiseList[i]).then(
          (value) => {
            return resolve(value)
          },
          (reason) => {
            return reject(reason)
          }
        )
      }
    })
  }
  // 所有都完成(无论是 resolved 或 rejected)才返回结果(包含每个 promise 的结果和状态)
  static allSettled(promiseList) {
    return new MyPromise((resolve, reject) => {
      const result = []
      const length = promiseList.length
      let count = 0

      if (length === 0) {
        return resolve(result)
      }

      for (let i = 0; i < length; i++) {
        const currentPromise = MyPromise.resolve(promiseList[i])
        currentPromise.then(
          (value) => {
            count++
            result[i] = {
              status: FULFILLED,
              value
            }
            if (count === length) return resolve(result)
          },
          (reason) => {
            count++
            result[i] = {
              status: REJECTED,
              value
            }
            if (count === length) return resolve(result)
          }
        )
      }
    })
  }
  // 这个方法和 Promise.all() 是相反的。
  // 1. 传入空的可迭代对象 返回已失败 rejected
  // 2. 传入的参数不包含 promise,返回异步完成 resolved
  // 3. 任何一个成功或所有都失败,返回 resolved 或 reject
  //
  // https://github.com/es-shims/Promise.any/blob/main/implementation.js
  static any(values) {
    return new MyPromise((resolve, reject) => {
      let hasResolved = false
      let promiseLikes = []
      let iterableCount = 0
      let rejectionReasons = []

      function resolveOnce(value) {
        if (!hasResolved) {
          hasResolved = true
          resolve(value)
        }
      }

      function rejectionCheck(reason) {
        // TODO: 这样不能保证结果的顺序
        rejectionReasons.push(reason)
        if (rejectionReasons.length >= iterableCount) reject(rejectionReasons)
      }

      for (let value of values) {
        iterableCount++
        promiseLikes.push(value)
      }

      for (let promiseLike of promiseLikes) {
        if (promiseLike.then !== undefined || promiseLike.catch !== undefined) {
          promiseLike.then((result) => resolveOnce(result)).catch((err) => {})
          promiseLike.catch((reason) => rejectionCheck(reason))
        } else {
          resolveOnce(promiseLike)
        }
      }
    })
  }
  // Promise.try 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API
  // https://es6.ruanyifeng.com/#docs/promise#Promise-try
  // https://github.com/tc39/proposal-promise-try/blob/main/polyfill.js
  static try(func) {
    return new MyPromise((resolve, reject) => {
      resolve(func())
    })
  }

  // 让同步函数同步执行,异步函数异步执行 是一种场景
  // 还有一种场景,比如请求了接口,但数据做了缓存,两次返回,一次是异步,一次是同步
  // 参见 https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8%E5%BE%AE%E4%BB%BB%E5%8A%A1
}

function resolvePromise(p, x, resolve, reject) {
  if (p === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }

  // 按规范 2.3.3 ~ 2.3.4 对以下逻辑进行处理
  if (typeof x === 'object' || typeof x === 'function') {
    if (x === null) return resolve(x)

    let then
    try {
      then = x.then
    } catch (err) {
      return reject(err)
    }

    if (typeof then === 'function') {
      let called = false
      try {
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(p, y, resolve, reject)
          },
          (r) => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } catch (err) {
        if (called) return
        reject(err)
      }
    } else {
      resolve(x)
    }
  } else {
    resolve(x)
  }
}

// 单测需要附加以下代码
MyPromise.deferred = function () {
  const result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = MyPromise

注意

ES6 Promise 是基于 EcmaScript 规范 而不是 Promise A+ 规范 来实现的

手写了 Promise 已经包含了所有功能,再熟悉下,仅实现以下功能呢

  1. 手写 Promise.all
  2. 手写 Promise.race
  3. 手写 Promise.allSettled
  4. 手写 Promise.any
  5. 手写 Promise.try
  6. 手写 Promise.deferred
  7. 手写 Promise.map
  8. p-limit
  9. p-queue
  10. p-throttle
js
MyPromise.prototype.finally = function (cb) {
  return this.then(
    (value) => {
      return MyPromise.resolve(fn()).then(() => value)
    },
    (err) => {
      return MyPromise.resolve(fn()).then(() => {
        throw err
      })
    }
  )
}
js
// 1. 传入空的可迭代对象 返回已完成 resolved
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 所有都都成功或任何一个失败,就变为 resolved 或 reject
export function pAll(promiseList = []) {
  return new Promise((resolve, reject) => {
    const result = []
    const length = promiseList.length
    let count = 0

    if (length === 0) {
      return resolve(result)
    }

    promiseList.forEach((promise, index) => {
      Promise.resolve(promise).then(
        (value) => {
          count++
          result[index] = value
          if (count === length) {
            resolve[result]
          }
        },
        (reason) => {
          reject(reason)
        }
      )
    })
  })
}
js
// 1. 传入空的可迭代对象,则返回的 promise 将永远等待
// 2. 返回第一个结果值(无论 resolved 或 rejected)
export function pRace(promiseList = []) {
  return new Promise((resolve, reject) => {
    const length = promiseList.length

    if (length === 0) {
      return // 永远 pending 中
      // 思考 这种永远 pending 会有什么影响
      // return resolve()
    }
    for (let i = 0; i < length; i++) {
      Promise.resolve(promiseList[i]).then(
        (value) => {
          return resolve(value)
        },
        (reason) => {
          return reject(reason)
        }
      )
    }
  })
}
js
// 所有都完成(无论是 resolved 或 rejected)
// 才返回结果(包含每个 promise 的结果和状态)
export function pAllSettled(promiseList = []) {
  return new Promise((resolve, reject) => {
    const result = []
    const length = promiseList.length
    let count = 0

    if (length === 0) {
      return resolve(result)
    }

    for (let i = 0; i < length; i++) {
      const currentPromise = Promise.resolve(promiseList[i])
      currentPromise.then(
        (value) => {
          count++
          result[i] = {
            status: FULFILLED,
            value
          }
          if (count === length) return resolve(result)
        },
        (reason) => {
          count++
          result[i] = {
            status: REJECTED,
            value
          }
          if (count === length) return resolve(result)
        }
      )
    }
  })
}
js
// 这个方法和 Promise.all() 是相反的。
// 1. 传入空的可迭代对象 返回已失败 rejected
// 2. 传入的参数不包含 promise,返回异步完成 resolved
// 3. 任何一个成功或所有都失败,返回 resolved 或 reject
//
// https://github.com/es-shims/Promise.any/blob/main/implementation.js
export function pAny(values) {
  return new Promise((resolve, reject) => {
    let hasResolved = false
    let promiseLikes = []
    let iterableCount = 0
    let rejectionReasons = []

    function resolveOnce(value) {
      if (!hasResolved) {
        hasResolved = true
        resolve(value)
      }
    }

    function rejectionCheck(reason) {
      // TODO: 这样不能保证结果的顺序
      rejectionReasons.push(reason)
      if (rejectionReasons.length >= iterableCount) reject(rejectionReasons)
    }

    for (let value of values) {
      iterableCount++
      promiseLikes.push(value)
    }

    for (let promiseLike of promiseLikes) {
      if (promiseLike.then !== undefined || promiseLike.catch !== undefined) {
        promiseLike.then((result) => resolveOnce(result)).catch((err) => {})
        promiseLike.catch((reason) => rejectionCheck(reason))
      } else {
        resolveOnce(promiseLike)
      }
    }
  })
}
js
// Promise.try 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API
// https://es6.ruanyifeng.com/#docs/promise#Promise-try
// https://github.com/tc39/proposal-promise-try/blob/main/polyfill.js
export function pTry(func) {
  return new Promise((resolve, reject) => {
    resolve(func())
  })
}

// 让同步函数同步执行,异步函数异步执行 是一种场景
// 还有一种场景,比如请求了接口,但数据做了缓存,两次返回,一次是异步,一次是同步
// 参见 https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8%E5%BE%AE%E4%BB%BB%E5%8A%A1
js
// 返回 Deferred(延迟) 的 Promise(承诺)对象
// Deferred 主要是用于内部,来维护异步模型的状态
export function pDeferred() {
  const result = {}
  result.promise = new Promise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

// usage:
function asyncDoSomeing(flag, message) {
  var deferred = new pDeferred()
  setTimeout(function () {
    if (flag) {
      deferred.resolve(message)
    } else {
      deferred.reject({ code: 400, message: '拒绝' })
    }
  }, 3000)
  return deferred.promise
}
asyncDoSomeing(true, '测试执行成功').then(
  function (message) {
    console.log(message)
  },
  function (err) {
    console.log(err)
  }
)
js
// 实现限制 promise 并发数
export function pMap(list, mapper, concurrency = Infinity) {
  // list 为 Iterator,先转化为 Array
  list = Array.from(list)
  return new Promise((resolve, reject) => {
    let currentIndex = 0
    let result = []
    let resolveCount = 0
    let len = list.length
    function next() {
      const index = currentIndex
      currentIndex++
      Promise.resolve(list[index])
        .then((o) => mapper(o, index))
        .then((o) => {
          result[index] = o
          resolveCount++
        })
        .finally(() => {
          if (resolveCount === len) {
            resolve(result)
          }
          if (currentIndex < len) {
            next()
          }
        })
      // catch 时没处理
    }
    for (let i = 0; i < concurrency && i < len; i++) {
      next()
    }
  })
}

// usage:
const sleep = (...rest) =>
  new Promise((resolve) => setTimeout(resolve, ...rest))
const tasks = [1, 2, 3, 4, 5, 6]
console.time('pMap')
pMap(tasks, (task) => sleep(1000, task), 3).then((res) => {
  console.log(res)
  console.timeEnd('pMap')
})
js
// p-limit 是一个限制并发的库,传入任务,返回一个函数用于接收每一个异步任务,
// 保证无论传入多少个异步任务,都始终根据传入的并发数量来进行执行。
// 这里直接拿来参考学习,源码如下
// https://github.com/sindresorhus/p-limit
import Queue from 'yocto-queue'

export function pLimit(concurrency) {
  // 验证并发数的合法性
  if (
    !(
      (Number.isInteger(concurrency) ||
        concurrency === Number.POSITIVE_INFINITY) &&
      concurrency > 0
    )
  ) {
    throw new TypeError('Expected `concurrency` to be a number from 1 and up')
  }

  // 这个是链表实现的队列,enqueue 入队,dequeue 出队
  const queue = new Queue()
  let activeCount = 0

  const next = () => {
    activeCount--

    if (queue.size > 0) {
      queue.dequeue()()
    }
  }

  const run = async (fn, resolve, args) => {
    activeCount++

    const result = (async () => fn(...args))()

    resolve(result)

    try {
      await result
    } catch {}

    next()
  }

  const enqueue = (fn, resolve, args) => {
    queue.enqueue(run.bind(undefined, fn, resolve, args))
    ;(async () => {
      // This function needs to wait until the next microtask before comparing
      // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
      // when the run function is dequeued and called. The comparison in the if-statement
      // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
      await Promise.resolve()

      if (activeCount < concurrency && queue.size > 0) {
        queue.dequeue()()
      }
    })()
  }

  const generator = (fn, ...args) =>
    new Promise((resolve) => {
      enqueue(fn, resolve, args)
    })

  Object.defineProperties(generator, {
    activeCount: {
      get: () => activeCount
    },
    pendingCount: {
      get: () => queue.size
    },
    clearQueue: {
      value: () => {
        queue.clear()
      }
    }
  })

  return generator
}

// usage:
const limit = pLimit(2)
const tasks = [1, 2, 3].map((i) => () => {
  sleep(i)
})
Promise.all(tasks.map((task) => limit(task))).then((res) => {
  console.log(res)
})

// 我们自己也实现个,参考 pMap(这里用数组缓存任务)
function pLimit(promiseTasks = [], concurrency) {
  const tasks = Array.from(promiseTasks)
  return new Promise((resolve, reject) => {
    let result = []
    let currentIndex = 0
    let resolveCount = 0
    let len = tasks.length
    function next() {
      const index = currentIndex
      currentIndex++
      Promise.resolve(tasks[index]())
        .then((res) => {
          result[index] = res
          resolveCount++
        })
        .catch((err) => {
          result[index] = err
          resolveCount++
        })
        .finally(() => {
          // 此处 resolveCount === currentIndex
          if (resolveCount === len) resolve(result)
          if (currentIndex < len) next()
        })
    }

    for (let i = 0; i < concurrency && i < len; i++) {
      next()
    }
  })
}

// usage:
const sleep = (...rest) =>
  new Promise((resolve) => setTimeout(resolve, ...rest))
const tasks = [1, 2, 3, 4, 5, 6].map((task) => () => sleep(1000, task))
console.time('pLimit')
pLimit(tasks, 2).then((res) => {
  console.log(res)
  console.timeEnd('pLimit')
})

// 这实现与 pMap 有点一样啊?思考下差异
// 这里 PLimit 仅有 limit 功能,没有 map 功能,
// 而 pLimit 同时有 map 和 limit 功能

关于 Promise.try()

让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API

js
// 方案一
const f = () => console.log('now')
;(async () => f())()
console.log('next')
// now
// next

// 方案二
const f = () => console.log('now')
;(() => new Promise((resolve) => resolve(f())))()
console.log('next')
// now
// next

// Promise.try 提案
const f = () => console.log('now')
Promise.try(f)
console.log('next')
// now
// next

小练习

js
// 自己造的一个题(2022-07-26 by cloudyan)
Promise.resolve(Promise.reject(1))
  .catch((err) => {
    console.log('catch1:', err)
    return err
  })
  .then((res) => {
    console.log('then1:', res)
    return res
  })
  .finally((res) => {
    console.log('finally:', res)
    return res
  })
  .then((res) => {
    console.log('then2:', res)
    return res
  })
  .catch((err) => {
    console.log('catch2:', err)
    return err
  })

加强练习

其他