Bootstrap

手写Promise完整版

完整代码

先上代码,后续再逐个完成。

const STATE = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class MyPromise {
  #state = STATE.PENDING
  #result = undefined
  #handlers = []

  #resolve = (value) => {
    this.#changeState(STATE.FULFILLED, value)
  }
  #reject = (reason) => {
    this.#changeState(STATE.REJECTED, reason)
  }

  constructor(callback) {
    try {
      callback(this.#resolve, this.#reject)
    } catch (error) {
      reject(error)
    }
  }

  #changeState(state, data) {
    if (this.#state !== STATE.PENDING) {
      return
    }
    this.#state = state
    this.#result = data
    this.#runAll()
  }

  #runAll() {
    if (this.#state === STATE.PENDING) {
      return
    }
    while (this.#handlers.length) {
      this.#runOne(this.#handlers.shift())
    }
  }

  #runOne({ onFulfilled, onRejected, resolve, reject }) {
    queueMicrotask(() => {
      const isFulfilled = this.#state === STATE.FULFILLED
      const callback = isFulfilled ? onFulfilled : onRejected
      const settled = isFulfilled ? resolve : reject

      if (typeof callback !== 'function') {
        settled(this.#result)
        return
      }

      try {
        const data = callback(this.#result)
        if (this.#isPromiseLike(data)) {
          data.then(resolve, reject)
        } else {
          resolve(data)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  #isPromiseLike(value) {
    if (value === null) {
      return false
    }

    const valueType = typeof value

    if (valueType !== 'function' && valueType !== 'object') {
      return false
    }

    if (typeof value.then !== 'function') {
      return false
    }

    return true
  }

  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      this.#handlers.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
      this.#runAll()
    })
  }

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

  finally(onFinally) {
    return this.then(
      (data) => {
        onFinally()
        return data
      },
      (reason) => {
        onFinally()
        return reason
      }
    )
  }

  static #instance

  static {
    this.#instance = new this((res) => {
      res()
    })
  }

  static #each(promises, onPromise, onOther) {
    for (let i = 0; i < promises.length; i++) {
      const promise = promises[i]

      if (this.#instance.#isPromiseLike(promise)) {
        onPromise(promise, i)
      } else {
        onOther(promise, i)
      }
    }
  }

  static #useValue(len, settled) {
    const arr = []
    let arrRealLen = 0

    const isComplete = () => {
      if (arrRealLen !== len) {
        return
      }
      settled(arr)
    }
    
    const addValue = (value, i) => {
      arr[i] = value
      arrRealLen += 1
      isComplete()
    }

    return addValue
  }

  static resolve(value) {
    if (value instanceof this) {
      return value
    }

    return new this((resolve, reject) => {
      if (this.#instance.#isPromiseLike(value)) {
        value.then(resolve, reject)
      } else {
        resolve(value)
      }
    })
  }

  static reject(reason) {
    return new this((_, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    return new this((resolve, reject) => {
      const addValue = this.#useValue(promises.length, resolve)

      const onPromise = (promise, i) =>
        promise.then(
          (data) => {
            addValue(data, i)
          },
          (reason) => {
            reject(reason)
          }
        )

      this.#each(promises, onPromise, addValue)
    })
  }

  static race(promises) {
    return new this((resolve, reject) => {
      const onPromise = (promise) => {
        promise.then(resolve, reject)
      }

      this.#each(promises, onPromise, resolve)
    })
  }

  static allSettled(promises) {
    return new this((resolve) => {
      const addValue = this.#useValue(promises.length, resolve)

      const onPromise = (promise, i) => {
        promise.then(
          (value) => {
            addValue({ status: STATE.FULFILLED, value }, i)
          },
          (reason) => {
            addValue({ status: STATE.REJECTED, reason }, i)
          }
        )
      }

      const onOther = (value, i) => {
        addValue({ status: STATE.FULFILLED, value }, i)
      }

      this.#each(promises, onPromise, onOther)
    })
  }

  static any(promises) {
    return new this((resolve, reject) => {
      const addValue = this.#useValue(promises.length, reject)

      const onPromise = (promise, i) =>
        promise.then(
          (data) => {
            resolve(data)
          },
          (reason) => {
            addValue(reason, i)
          }
        )

      this.#each(promises, onPromise, resolve)
    })
  }
}

构造函数

  1. Promise 有种状态:pending(进行中)、 fulfilled( 已成功)、rejected(已失败),并且状态一旦发生改变就不会再变。
  2. Promise 的构造函数接收一个函数为参数,这个函数的有两个参数,分别是 resolve和 reject。resolve的作用是将 promise的状态从 pending转为 fulfilled, reject的作用是将 promise的状态从 pending转为 rejected。
  3. 构造函数接收的函数参数报错时,会将 promise的状态从 pending转为 rejected。
// 定义Promise的状态
const STATE = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class MyPromise {
  #state = STATE.PENDING, // promise的状态
  #result = undefined, // promise的结果
  
  // 由于 this指向问题,需要定义成箭头函数
  #resolve = (value) => {
    this.#changeState(STATE.FULFILLED, value)
  }
  #reject = (reason) => {
    this.#changeState(STATE.REJECTED, reason)
  }
  
  constructor(executor) {
    try {
      // 将 resolve和 reject传给 Promise的函数
      executor(this.#resolve, this.#reject)
    } catch (error) {
       // executor 报错时直接将状态转为 rejected
      reject(error)
    }
  }
  
  // 修改 promise的状态
  #changeState(state, data) {
    // 状态改变就不再发生变化
    if (this.#state !== STATE.PENDING) {
      return
    }
    this.#state = state
    this.#result = data
  }
}

then

  1. then 接收两个函数作为参数,第一个参数是在 promise状态为 fulfilled的时候执行,第二个参数时在 rejected的时候执行。

  2. then 返回一个新的 Promise实例。


class MyPromise {
  #state = STATE.PENDING,
  #result = undefined,
  #handlers = [] // 收集 then的参数和 then返回的promise的 resolve和 reject

  // ...  
  
  #changeState(state, data) {
    if (this.#state !== STATE.PENDING) {
      return
    }
    this.#state = state
    this.#result = data
    this.#runAll() // 调用一次,此时 promise状态已改变,如果 #handlers收集了函数,则可以执行
  }
  
  #runAll() {
  	// 如果是 pending状态就不执行
    if (this.#state === STATE.PENDING) {
      return
    }
    
    // 取出数组的第一项进行执行,直到数组为空
    while (this.#handlers.length) {
      this.#runOne(this.#handlers.shift())
    }
  }
  
  #runOne({ onFulfilled, onRejected, resolve, reject }) {
    // 执行一项 ...
  }
  
  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      // 由于此时不知道 promise的状态,且一个 promise实例可以多次调用 then,所以先用数组将函数收集,等待执行
	  this.#handlers.push({
		onFulfilled,
        onRejected,
        resolve,
        reject
	  })
	  this.runAll() // 调用一次,此时函数已收集,如果 promise状态已改变,则可以执行
    })
  }
}
  1. then 的参数放入微任务队列等待执行,如果传入的不是函数,则会将上次的结果透传给下一个 then,如果是函数就调用,执行时报错则直接返回失败的 promise,不报错则拿到函数的返回值,如果返回值是一个 thenable对象(具有then方法的对象),则调用它的 then方法,并将 resolve和 reject作为参数传给它,否则调用 resolve方法。
  #runOne({ onFulfilled, onRejected, resolve, reject }) {
    // 创建异步任务
    queueMicrotask(() => {
      // 根据 promise的状态选择对应的函数
      const isFulfilled = this.#state === STATE.FULFILLED
      const callback = isFulfilled ? onFulfilled : onRejected
      const settled = isFulfilled ? resolve : reject

	  // 如果不是函数,则透传给下一个then
      if (typeof callback !== 'function') {
        settled(this.#result)
        return
      }

      try {
        // 将上一个promise的结果传给 then的回调,并拿到回调的返回值
        const data = callback(this.#result)
        // 判断是不是 thenable对象
        if (this.#isPromiseLike(data)) {
          data.then(resolve, reject)
        } else {
          resolve(data)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  // 判断是不是 thenable对象
  #isPromiseLike(value) {
    // 如果为 null,则不是 thenable对象 (typeof null === 'object')
    if (value === null) {
      return false
    }
    
    const valueType = typeof value
    
	// 如果既不是函数也不是对象,则不是 thenable对象
    if (valueType !== 'function' && valueType !== 'object') {
      return false
    }

	// 如果它的 then方法不是函数,则不是 thenable对象
    if (typeof value.then !== 'function') {
      return false
    }
	
	// 否则为 thenable对象
    return true
  }

catch

  1. catch 用于捕获前面所抛出的错误,接收一个函数作为参数,作用等同于 then的第二个参数。
  2. catch 返回一个新的 Promise实例。
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

finally

  1. 不管 Promise 对象最后状态如何,都会执行的 finally里面的函数。
  2. finally 的回调函数不接受任何参数。
  3. finally 返回一个新的 Promise实例,并且值与它的上一个promise相同。
  finally(onFinally) {
    return this.then(
      (data) => {
        onFinally() // 运行回调
        return data // 将上一个promise传过来的参数返回,透传给下一个promise
      },
      (reason) => {
        onFinally()
        return reason
      }
    )
  }

resolve

作用:将数据转成一个promise对象

  1. 参数是一个 Promise实例时,直接返回这个实例。
  2. 参数时一个 thenable对象时,返回一个新的 Promise实例,并将这个实例的 resolve和 reject传给这个对象的 then方法。
  3. 参数时其他数据时,返回一个新的 Promise实例,并将这个参数作为实例的结果。
  // 定义一个实例,方便再静态方法中调用实例方法
  static #instance

  // 静态块 在类生成时运行,且只运行一次,对静态属性赋值
  static {
    // 静态方法里面的 this指向构造函数
    this.#instance = new this((res) => {
      res()
    })
  }

  static resolve(value) {
    // 参数时 Promise实例
    if (value instanceof this) {
      return value
    }
	
    return new this((resolve, reject) => {
      // 参数是 thenable
      if (this.#instance.#isPromiseLike(value)) {
        value.then(resolve, reject)
      } else {
        resolve(value)
      }
    })
  }

reject

作用:返回一个新的 Promise实例,该实例的状态为 rejected。

  static reject(reason) {
    return new this((_, reject) => {
      reject(reason)
    })
  }

all

  1. 接收有个数组作为参数,数组的每一个都是 Promise实例,如果不是,则将其转为成功的 promise。
  2. 返回一个新的 Promise实例。
  3. 参数数组中所有 promise都为 fulfilled状态时,返回的 Promise实例状态也为 fulfilled,否则为 rejected。

由于 all、race、allSettled、any都需要对参数数组循环判断每一项是否为 Promise实例,所以先创建函数用来判断并根据不同情况处理。

  // 接收三个参数,第一个是 Promise实例数组,第二个是数组项为 Promise实例的回调,第三个是数组项不是 Promise实例的回调。
  static #each(promises, onPromise, onOther) {
    for (let i = 0; i < promises.length; i++) {
      const promise = promises[i]

	  // 判断是否为 promise
      if (this.#instance.#isPromiseLike(promise)) {
        onPromise(promise, i)
      } else {
        onOther(promise, i)
      }
    }
  }

all、allSettled、any都会返回结果为数组的 Promise实例,所以可以创建函数实现复用。

  // 第一个参数是 promise数组的长度,第二个是 resolve或 reject,根据需求传,用来改变返回的 Promise实例的状态
  static #useValue(len, settled) {
    const arr = [] // 收集状态改变的 promise,并作为结果返回
    let arrRealLen = 0 // 已收集的个数 (不能保证数组中的 promise是按顺序完成的,所以不能使用 arr.length)

	// 如果 promise状态全部改变,则改变返回的 Promise实例的状态
    const isComplete = () => {
      if (arrRealLen !== len) {
        return
      }
      settled(arr)
    }
    
    // 数组项的 promise状态改变后添加到数组,并判断是否全部收集完成
    const addValue = (value, i) => {
      arr[i] = value
      arrRealLen += 1
      isComplete()
    }

    return addValue
  }

工具函数完成后,接下来就是 Promise.all了

  static all(promises) {
    // 返回一个新的 Promise实例
    return new this((resolve, reject) => {
      // 将 resolve传给工具函数,当收集完成时自动调用,并获取向数组添加值的方法
      const addValue = this.#useValue(promises.length, resolve)

      const onPromise = (promise, i) =>
        promise.then(
          // 当前 promise状态为 fulfilled时,将结果添加到数组
          (data) => {
            addValue(data, i)
          },
          // 当前 promise状态为 rejected时,直接改变调用 reject改变返回的Promise实例的状态
          (reason) => {
            reject(reason)
          }
        )

	  // 对参数数组项进行处理
      this.#each(promises, onPromise, addValue)
    })
  }

race

  1. 接收一个数组作为参数,且返回一个新的 Promise实例,状态和数组中第一个状态改变的 promise相同。
  static race(promises) {
    return new this((resolve, reject) => {
      // 如果是 promise,则根据它的状态调用 resolve或 reject
      const onPromise = (promise) => {
        promise.then(resolve, reject)
      }
	  // 如果不是 promise,直接调用 resolve
      this.#each(promises, onPromise, resolve)
    })
  }

allSettled

  1. 当参数数组中所有 promise状态改变时,返回一个状态为 fulfilled的Promise实例
  static allSettled(promises) {
    return new this((resolve) => {
      const addValue = this.#useValue(promises.length, resolve)

      const onPromise = (promise, i) => {
        // 不管 promise状态时 fulfilled还是 rejected,都向数组中添加
        promise.then(
          (value) => {
            addValue({ status: STATE.FULFILLED, value }, i)
          },
          (reason) => {
            addValue({ status: STATE.REJECTED, reason }, i)
          }
        )
      }

      const onOther = (value, i) => {
        addValue({ status: STATE.FULFILLED, value }, i)
      }

      this.#each(promises, onPromise, onOther)
    })
  }

any

  1. any与all相反,参数数组中所有 promise都为 rejected状态时,返回的 Promise实例状态也为 rejected,否则为 fulfilled。
  static any(promises) {
    return new this((resolve, reject) => {
      const addValue = this.#useValue(promises.length, reject)

      const onPromise = (promise, i) =>
        promise.then(
          // 当前 promise状态为 fulfilled时,直接改变调用 resolve
          (data) => {
            resolve(data)
          },
          // 当前 promise状态为 rejected时,将结果添加到数组
          (reason) => {
            addValue(reason, i)
          }
        )

      this.#each(promises, onPromise, resolve)
    })
  }

总结

  1. 类中可以用 # 定义私有属性和方法。
  2. 静态方法中的 this指向构造函数,实例方法中的 this指向实例。
  3. 如果想在静态方法中调用实例方法,可以定义一个静态属性接收实例(单例模式),并在静态块在给它赋值(只会运行一次)。
  4. queueMicrotask可以将任务添加到微任务队列,也可以使用 process.nextTick(node环境)、MutationObserver(浏览器环境)。Vue.nextTick将任务加到微队列的方法优先级分别是:Promise > MutationObserver > setImmediate > setTimeout。
;