So let’s look at the specification

  • 1. A promise must provide onethenMethod to get the current or final value or Reason

A promise’s then method accepts two parameters: promise. Then (onFulfilled, onRejected)

  • 2. Promise state

A promise has only one state (pending, depressing, one of Rejected). There are only two possibilities for the state of the promise object to change from pending to fulfilled and from pending to rejected

  • 3. See here for more specific criteria

Create the MyPromise constructor

const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class MyPromise { Constructor (executor) {this.status = PENDING // Current status this.value = null // Successful value this.reason = null // failed value Enclosing onFulfilledCallbacks = [] / / success callback enclosing onRejectedCallbacks = [] / / failure callback / / resolve and reject why use arrow function? // Resolve = (value) => {// Only state is waiting, This. Status === PENDING) {this.status = pity // This Resolve it will take out all the success callback execution enclosing onFulfilledCallbacks. ForEach ((fn) = > fn (value))}} const reject = (reason) = > {the if (this.status === PENDING) {this.status = REJECTED // Change status this.reason = reason // Resolve all failed callbacks This. OnRejectedCallbacks. ForEach ((fn) = > fn (reason))}} try {/ / execution executor (resolve, reject) } catch (error) { reject(error) } }Copy the code

Then method

then(onFulfilled, OnFulfilled === 'function'? // This is a big pity and onFulfilled === 'function'? onFulfilled : (value) => value const realonRejected = typeof onRejected === 'function' ? onRejected : (reason) => {throw reason} const promise2 = new MyPromise((resolve), Reject) => {// Successful microtask const ledMicroTask = () => {// Create a microtask and wait for promise2 to complete. Initialize queueMicrotask(() => {try {// This will be a big pity (this.value) // This will be a big pity (promise, x, resolve, Reject)} Catch (error) {reject(error)}})} // Failed microtask const rejectMicrotask = () => {// Create a microtask to wait for the initialization of promise2 to complete QueueMicrotask (() => {try {const x = realonRejected(this.reason) // Pass resolvePromise resolvePromise(promise2, x, resolve, (this. Status === very depressing) {ledmicrotask ()} else If (this.status === PENDING) {rejectMicrotask()} else if (this.status === PENDING) {this.status === PENDING; So the success callback callbacks and failure stored / / wait for implementing successful failure function transfer this. When onFulfilledCallbacks. Push (fulfilledMicrotask) this.onRejectedCallbacks.push(rejectMicrotask) } }) return promise2 }Copy the code

3. ResolvePromise auxiliary method

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) } }Copy the code

Four, test,

  • 1. Value penetration test

Value penetration refers to the fact that when the arguments of a chain call are not functions, value penetration occurs and the non-function values passed in are ignored instead of the previous function arguments.

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****1Copy the code
  • 2. AllSettled test

ES2020 introduces the promise.allSettled () method, which is used to determine if a set of asynchronous operations are all over (whether they succeed or fail). Thus, it is “Settled”, which includes “depressing” and “Rejected”

// allResolved test 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 } // ]Copy the code
    1. Any test

ES2021 introduces the promise.any () method. This method accepts a group of Promise instances as parameters, and wraps them into a new Promise instance. If one of the parameter instances becomes a pity state, the packaging instance will become a pity state. If all parameter instances become the Rejected state, the wrapper instance becomes the Rejected state.

// any test 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] });Copy the code
  • 4 Official test cases

Execute $romises-aplus-tests mypromose.js

Pass 872 blueway-aplus-Tests

Promise/A+ Official documentation: promisesaplus.com/

The complete code
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

Copy the code