手写 Promise 源码

155 阅读3分钟

简易版 Promise

const PENDING = 'PENDING',
  FULFILLED = 'FULFILLED',
  REJECTED = 'REJECTED'
class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
      }
    }
    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }
}
module.exports = MyPromise

测试一下

const MyPromise = require('./MyPromise')
let promise = new MyPromise((resolve, reject) => {
  resolve('success')
  // reject('error')
})
promise.then(
  (value) => {
    console.log('Fulfilled', value) // Fulfilled success
  },
  (reason) => {
    console.log('Rejected', reason) // Rejected error
  }
)

但是当我们在实例中抛出错误的时候,MyPromise 没有自动捕获错误。

const MyPromise = require('./MyPromise')
let promise = new MyPromise((resolve, reject) => {
  throw new Error('Error!') // 此时程序无法捕获错误
})

修改为 用 try { } catch (e) { } 捕获 executor 执行过程中的错误

......
class MyPromise {
 ......
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  ......
}

module.exports = MyPromise

处理 Promise 中的异步和多次调用问题

let promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 2000)
})

promise.then(
  (value) => {
    console.log('Fulfilled', value)  //  **没有反应**
  }
)

此时的 MyPromise 还不能处理异步的,增加程序的异步处理功能,我们需要用到订阅发布的程序设计模式。当处于 PENDING 状态的时候,收集成功和失败的回调函数,这个过程称为订阅过程,然后在 FULFILLED 状态或者 REJECTED 状态的时候,触发收集的回调函数,即发布过程。

......
class MyPromise {
  constructor(executor) {
    ......
    this.onFulfilledCallback = [] // 存储成功的回调函数
    this.onRejectedCallback = [] // 存储失败的回调函数

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
        // 发布
        for (let i = 0; i < this.onFulfilledCallback.length; i++) {
          this.onFulfilledCallback[i]()
        }
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
        // 发布
        for (let i = 0; i < this.onRejectedCallback.length; i++) {
          this.onRejectedCallback[i]()
        }
      }
    }
    ......
  }

  then(onFulfilled, onRejected) {
    ......
    if (this.status === PENDING) {
      // 在PENDING状态收集回调函数,这个过程称为订阅的过程
      this.onFulfilledCallback.push(() => {
        onFulfilled(this.value)
      })
      this.onRejectedCallback.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

测试一下,此时程序可以执行异步函数了

let promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 2000)
})

promise.then(
  (value) => {
    console.log('Fulfilled1', value)
  },
  (reason) => {
    console.log('Rejected1', reason)
  }
)

promise.then(
  (value) => {
    console.log('Fulfilled2', value)
  },
  (reason) => {
    console.log('Rejected2', reason)
  }
)

// Fulfilled1 success
// Fulfilled2 success

实现Promise 的链式调用功能

实现起来有点复杂,建议参照 Promises/A+ 规范,一步步实现就很清晰啦~

const PENDING = 'PENDING',
  FULFILLED = 'FULFILLED',
  REJECTED = 'REJECTED'
class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined

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

    const resolve = (value) => {
      if (value instanceof MyPromise) {
        return value.then(resolve, reject)
      }
      setTimeout(() => {
        if (this.status === PENDING) {
          this.status = FULFILLED
          this.value = value
          // 发布
          for (let i = 0; i < this.onFulfilledCallback.length; i++) {
            this.onFulfilledCallback[i]()
          }
        }
      }, 0)
    }
    const reject = (reason) => {
      setTimeout(() => {
        if (this.status === PENDING) {
          this.status = REJECTED
          this.reason = reason
          // 发布
          for (let i = 0; i < this.onRejectedCallback.length; i++) {
            this.onRejectedCallback[i]()
          }
        }
      }, 0)
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === PENDING) {
        // 在PENDING状态收集回调函数,这个过程称为订阅的过程
        // PENDING状态不用延时
        this.onFulfilledCallback.push(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        this.onRejectedCallback.push(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
    })
    return promise2
  }

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

function resolvePromise(promise2, x, resolve, reject) {
  // 按照 Promises/A+ 规范一步步实现
  if (promise2 === x) {
    return reject(new TypeError('circle!')) // 处理死循环
  }

  if (x instanceof MyPromise) {
    if (this.status === PENDING) {
      x.then((v) => {
        resolvePromise(promise2, v, resolve, reject)
      }, reject)
    } else {
      x.then(resolve, reject)
    }
    return
  }

  let called = false
  if (
    (x !== 'null' && typeof x === 'object') ||
    typeof x === 'function'
  ) {
    try {
      let then = x.then
      if (typeof then === 'function') {
        // Promise
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(promise2, y, resolve, reject)
          },
          (r) => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}

module.exports = MyPromise

实现过程可以参考B站这位大佬的视频 www.bilibili.com/video/BV1nV…

虽然 then 普遍认为是微任务。但是浏览器没办法模拟微任务,目前要么用 setImmediate ,这个也是宏任务,且不兼容的情况下还是用 setTimeout 打底的。还有,promise 的 polyfill (es6-promise) 里用的也是 setTimeout。因此这里就直接用 setTimeout,以宏任务来代替微任务了。 此段文字转自 juejin.cn/post/684490…