手撸Promise/A+ 规范

321 阅读3分钟

先看看规范

  • 1、一个 promise 必须提供一个 then 方法,用来获取当前或最终的 value 或 reason

一个 promise 的 then 方法接受两个参数: promise.then(onFulfilled, onRejected)

  • 2、Promise 状态

一个 promise 有且只有一个状态(pending,fulfilled,rejected 其中之一),Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected

一、创建MyPromise构造函数

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

class MyPromise {
  constructor(executor) {
    this.status = PENDING // 当前状态
    this.value = null // 成功的值
    this.reason = null // 失败的值
    this.onFulfilledCallbacks = [] //成功回调
    this.onRejectedCallbacks = [] // 失败回调
    // resolve和reject为什么要用箭头函数?
    // 如果直接调用的话,普通函数this指向的是window或者undefined
    // 用箭头函数就可以让this指向当前实例对象
    const resolve = (value) => {
      // 只有状态是等待,才执行状态修改
      if (this.status === PENDING) {
        this.status = FULFILLED // 更改状态
        this.value = value // 成功赋值
        // resolve里面将所有成功的回调拿出来执行
        this.onFulfilledCallbacks.forEach((fn) => fn(value))
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED // 更改状态
        this.reason = reason // 失败赋值
        // resolve里面将所有失败的回调拿出来执行
        this.onRejectedCallbacks.forEach((fn) => fn(reason))
      }
    }
    try {
      // 执行
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

二、then 方法

 then(onFulfilled, onRejected) {
    // 保证onFulfilled 和onRejected为一个函数
    const realOnfulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    const realonRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }
    // 链式调用,直接return出去promise2
    const promise2 = new MyPromise((resolve, reject) => {
      // 成功的微任务
      const fulfilledMicrotask = () => {
        // 创建一个微任务等待promise2完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功的结果
            const x = realOnfulfilled(this.value)
            // 传入resolvePromise集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // 失败的微任务
      const rejectMicrotask = () => {
        // 创建一个微任务等待promise2完成初始化
        queueMicrotask(() => {
          try {
            // 获取失败的结果
            const x = realonRejected(this.reason)
            // 传入resolvePromise集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // 当前状态做判断
      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectMicrotask()
      } else if (this.status === PENDING) {
        // 等待
        // 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
        // 等到执行成功失败函数的时候再传递
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectMicrotask)
      }
    })
    return promise2
  }

三、resolvePromise辅助方法


function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('cycle....'))
  }
  if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
    let then
    try {
      then = x.then
    } catch (error) {
      return reject(error)
    }
    if (typeof then === 'function') {
      let called = false
      try {
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(promise, y, resolve, reject)
          },
          (r) => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      resolve(x)
    }
  } else {
    resolve(x)
  }
}

四、测试

  • 1.值穿透 测试

值穿透指的是,链式调用的参数不是函数时,会发生值穿透,就传入的非函数值忽略,传入的是之前的函数参数。

// 值穿透 测试
MyPromise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)
    // 1-
MyPromise.resolve(1)
    .then(() => { return 2 })
    .then(() => { return 3 })
    .then(console.log)

    // 3

MyPromise.resolve(1)
    .then(function () {
        return 2
    })
    .then(() => { Promise.resolve(3) })
    .then(console.log)
    // undefined

MyPromise.reject(1)
    .then(res => {
        console.log(res);
    })
    .then(res => { console.log(res) },
        rej => {
            console.log(`rej****${rej}`);
        })
    .catch(err => {
        console.log(`err****${err}`);
    })
// rej****1


  • 2.allSettled测试 ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况
// allSettled 测试
const resolved = MyPromise.resolve(42);
const rejected = MyPromise.reject(-1);

const allSettledPromise = MyPromise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//   { status: 'fulfilled', value: 42 },
//   { status: 'rejected', reason: -1 }
// ]

    1. any 测试 ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
// any 测试
const resolvedAny = MyPromise.resolve(42);
const rejectedAny = MyPromise.reject(-1);
const alsoRejected = MyPromise.reject(Infinity);

MyPromise.any([resolvedAny, rejectedAny, alsoRejected]).then(function (result) {
  console.log(result); // 42
});
MyPromise.any([rejectedAny, alsoRejected]).catch(function (results) {
  console.log(results); // [-1, Infinity]
});
  • 4 官方测试用例 执行 $ romises-aplus-tests myPromose.js

promises-aplus-tests测试通过872测试用例

image.png

Promise/A+官方官方文档: promisesaplus.com/

完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    this.status = PENDING // 当前状态
    this.value = null // 成功的值
    this.reason = null // 失败的值
    this.onFulfilledCallbacks = [] //成功回调
    this.onRejectedCallbacks = [] // 失败回调
    // resolve和reject为什么要用箭头函数?
    // 如果直接调用的话,普通函数this指向的是window或者undefined
    // 用箭头函数就可以让this指向当前实例对象
    const resolve = (value) => {
      // 只有状态是等待,才执行状态修改
      if (this.status === PENDING) {
        this.status = FULFILLED // 更改状态
        this.value = value // 成功赋值
        // resolve里面将所有成功的回调拿出来执行
        this.onFulfilledCallbacks.forEach((fn) => fn(value))
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED // 更改状态
        this.reason = reason // 失败赋值
        // resolve里面将所有失败的回调拿出来执行
        this.onRejectedCallbacks.forEach((fn) => fn(reason))
      }
    }
    try {
      // 执行
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    // 保证onFulfilled 和onRejected为一个函数
    const realOnfulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    const realonRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }
    // 链式调用,直接return出去promise2
    const promise2 = new MyPromise((resolve, reject) => {
      // 成功的微任务
      const fulfilledMicrotask = () => {
        // 创建一个微任务等待promise2完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功的结果
            const x = realOnfulfilled(this.value)
            // 传入resolvePromise集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // 失败的微任务
      const rejectMicrotask = () => {
        // 创建一个微任务等待promise2完成初始化
        queueMicrotask(() => {
          try {
            // 获取失败的结果
            const x = realonRejected(this.reason)
            // 传入resolvePromise集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // 当前状态做判断
      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectMicrotask()
      } else if (this.status === PENDING) {
        // 等待
        // 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
        // 等到执行成功失败函数的时候再传递
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectMicrotask)
      }
    })
    return promise2
  }
  // 失败的错误捕获
  // Promise.prototype.catch()方法是.then(null, rejection)
  // 或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
  catch(onRejected) {
    this.then(undefined, onRejected)
  }
  // finally()方法用于指定不管 Promise 对象最后状态如何,
  // 都会执行的操作。该方法是 ES2018 引入标准的。
  finally(fn) {
    return this.then(
      (value) => {
        return MyPromise.resolve(fn()).then(() => {
          return value
        })
      },
      (reason) => {
        return MyPromise.resolve(fn()).then(() => {
          throw reason
        })
      }
    )
  }
  // resolve 静态方法 通过 MyPromise.resolve()调用
  static resolve(parmeter) {
    // 如果传入 MyPromise 就直接返回
    if (parmeter instanceof MyPromise) {
      return parmeter
    }
    return new MyPromise((resolve) => {
      resolve(parmeter)
    })
  }
  // reject 静态方法 通过 MyPromise.reject()调用
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
  // all的静态方法 通过 MyPromise.all()调用
  // 只要参数实例有一个变成rejected状态,包装实例就会变成rejected状态;
  // 如果所有参数实例都变成fulfilled状态,包装实例就会变成fulfilled状态。
  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) => {
        // 保证是一个promise
        MyPromise.resolve(promise).then(
          (value) => {
            count++
            result[index] = value
            if (count === length) {
              resolve(result)
            }
          },
          (reason) => {
            reject(reason)
          }
        )
      })
    })
  }
  // any 的静态方法 通过 MyPromise.any()调用 es2021加入 和all刚好相反
  // 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;
  // 如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
  static any(promiseList) {
    return new MyPromise((resolve, reject) => {
      let count = 0
      let result = []
      let len = promiseList.length
      if (len === 0) return resolve(result)
      promiseList.forEach((item) => {
        // 保证是一个promise
        MyPromise.resolve(item).then(
          (value) => {
            resolve(value)
          },
          (reason) => {
            count++
            result.push(reason)
            if (count === len) {
              reject(result)
            }
          }
        )
      })
    })
  }
  // allSettled 的静态方法 通过 MyPromise.allSettled()调用
  // ES2020 引入了Promise.allSettled()方法 用来确定一组异步操作是否都结束了(不管成功或失败)
  //返回格式: [
  //    { status: 'fulfilled', value: value},
  //    { status: 'rejected', reason: reason }
  // ]
  static allSettled(promiseList) {
    return new MyPromise((resolve) => {
      const length = promiseList.length
      const result = []
      let count = 0

      if (length === 0) {
        return resolve(result)
      } else {
        for (let i = 0; i < length; i++) {
          const currentPromise = MyPromise.resolve(promiseList[i])
          currentPromise.then(
            (value) => {
              count++
              result[i] = {
                status: 'fulfilled',
                value: value,
              }
              if (count === length) {
                return resolve(result)
              }
            },
            (reason) => {
              count++
              result[i] = {
                status: 'rejected',
                reason: reason,
              }
              if (count === length) {
                return resolve(result)
              }
            }
          )
        }
      }
    })
  }
  // race 的静态方法 通过 MyPromise.race()调用
  // 只要有个实例决议后就返回改该决议后的结果
  static race(promiseList) {
    return new MyPromise((resolve, reject) => {
      const length = promiseList.length

      if (length === 0) {
        return resolve()
      } else {
        for (let i = 0; i < length; i++) {
          MyPromise.resolve(promiseList[i]).then(
            (value) => {
              return resolve(value)
            },
            (reason) => {
              return reject(reason)
            }
          )
        }
      }
    })
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('cycle....'))
  }
  if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
    let then
    try {
      then = x.then
    } catch (error) {
      return reject(error)
    }
    if (typeof then === 'function') {
      let called = false
      try {
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(promise, y, resolve, reject)
          },
          (r) => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      resolve(x)
    }
  } else {
    resolve(x)
  }
}
MyPromise.deferred = function () {
  var result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

// 值穿透 测试
MyPromise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)
    // 1-
MyPromise.resolve(1)
    .then(() => { return 2 })
    .then(() => { return 3 })
    .then(console.log)

    // 3

MyPromise.resolve(1)
    .then(function () {
        return 2
    })
    .then(() => { Promise.resolve(3) })
    .then(console.log)
    // undefined

MyPromise.reject(1)
    .then(res => {
        console.log(res);
    })
    .then(res => { console.log(res) },
        rej => {
            console.log(`rej****${rej}`);
        })
    .catch(err => {
        console.log(`err****${err}`);
    })
// rej****1

// allSettled 测试
const resolved = MyPromise.resolve(42);
const rejected = MyPromise.reject(-1);

const allSettledPromise = MyPromise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//   { status: 'fulfilled', value: 42 },
//   { status: 'rejected', reason: -1 }
// ]
// any 测试
const resolvedAny = MyPromise.resolve(42);
const rejectedAny = MyPromise.reject(-1);
const alsoRejected = MyPromise.reject(Infinity);

MyPromise.any([resolvedAny, rejectedAny, alsoRejected]).then(function (result) {
  console.log(result); // 42
});
MyPromise.any([rejectedAny, alsoRejected]).catch(function (results) {
  console.log(results); // [-1, Infinity]
});
module.exports = MyPromise