从零手撕Promise,掌握Promise的实现原理(6)之then方法的回调为什么是异步微任务

432 阅读3分钟

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。

传送门

从零手撕Promise,掌握Promise的实现原理(1)之promise基本结构的实现 从零手撕Promise,掌握Promise的实现原理(2)之基础版本的promise实现 从零手撕Promise,掌握Promise的实现原理(3)之回调地狱是什么 从零手撕Promise,掌握Promise的实现原理(4)之then方法链式调用的初步实现 从零手撕Promise,掌握Promise的实现原理(5)之then方法链式调用的进阶实现

回顾

经过上一篇文章的介绍我们的promise现在已经长这样了

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise, x, resolve, reject) => {

}
class Promise{
  constructor(executor){

    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    //存放onFulfilled
    this.onResolvedCallbacks = []
    //存放onRejected
    this.onRejectedCallbacks = []
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.value = value
        this.state = FULFILLED
        //promise实例状态改变后调用暂存的onFulfilled
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.reason = reason
        this.state = REJECTED
        //promise实例状态改变后调用的onRejected
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      //executor函数执行过程中出错,将会导致Promise失败
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected){

    let promise = new Promise((resolve, reject) => {
        
        switch(this.state){
          case FULFILLED:
              try{
                let x = onFulfilled(this.value)
                resolvePromise(promise, x, resolve, reject)
              } catch(e){
                reject(e)
              }   
              break
          case REJECTED:
              try{
                let x = onRejected(this.reason)
                resolvePromise(promise, x, resolve, reject)
              } catch(e){
                reject(e)
              }   
              break
          default:
            this.onResolvedCallbacks.push(() => {
              try{
                let x = onFulfilled(this.value)
                resolvePromise(promise, x, resolve, reject)
              } catch(e){
                reject(e)
              }
            })
            this.onRejectedCallbacks.push(() => {
              try{
                let x = onRejected(this.reason)
                resolvePromise(promise, x, resolve, reject)
              } catch(e){
                reject(e)
              }
            })
        }
      
    })
    return promise
  }
}

为什么then方法中的回调(onFulfilled、onRejected)是微任务?

11-06-1.png

  • 像上面图片展示的那样,当代码执行到resolvePromise的时候其实我们是拿不到promise(序号2)的,这样去访问控制台会报错。

11-07-1.png 这个错误的意思是说,初始化前无法访问promise,因为对于Promise(序号2)来说,此时Promise(序号2)还处于自身ecexutor函数执行的时候,还没有初始化完成,也就是说此时还不存在这个实例。那么我们后续在resolvePromise函数中是无法拿到promise(序号2)的。

  • 这也是为什么then方法中的回调(onFulfilled、onRejected)是异步微任务,我们可以通过浏览器提供的queueMicrotask方法去创建一个异步微任务,但是这个方法的兼容性不是很好,我们也可以用setTimeout异步宏任务去替代它,那么我们的then方法改造如下:
then(onFulfilled, onRejected){

    let promise = new Promise((resolve, reject) => {
        
          switch(this.state){
            case FULFILLED:
                setTimeout(() => {
                  try{
                    let x = onFulfilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })   
                break
            case REJECTED:
                setTimeout(() => {
                  try{
                    let x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })   
                break
            default:
              this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                  try{
                    let x = onFulfilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })
              })
              this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                  try{
                    let x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })
              })
          }
      })
    return promise
  }
  • 经过这番改造后,我们在resolvePromise函数中就可以拿到promise进行接下来的操作了

那么我们的Promise现在变成了这样

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise, x, resolve, reject) => {

}
class Promise{
  constructor(executor){

    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    //存放onFulfilled
    this.onResolvedCallbacks = []
    //存放onRejected
    this.onRejectedCallbacks = []
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.value = value
        this.state = FULFILLED
        //promise实例状态改变后调用暂存的onFulfilled
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.reason = reason
        this.state = REJECTED
        //promise实例状态改变后调用的onRejected
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      //executor函数执行过程中出错,将会导致Promise失败
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected){

    let promise = new Promise((resolve, reject) => {
        
          switch(this.state){
            case FULFILLED:
                setTimeout(() => {
                  try{
                    let x = onFulfilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })   
                break
            case REJECTED:
                setTimeout(() => {
                  try{
                    let x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })   
                break
            default:
              this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                  try{
                    let x = onFulfilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })
              })
              this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                  try{
                    let x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                  } catch(e){
                    reject(e)
                  }
                })
              })
          }
      })
    return promise
  }
}

下篇文章继续完成相关内容

传送门

从零手撕Promise,掌握Promise的实现原理(1)之promise基本结构的实现 从零手撕Promise,掌握Promise的实现原理(2)之基础版本的promise实现 从零手撕Promise,掌握Promise的实现原理(3)之回调地狱是什么 从零手撕Promise,掌握Promise的实现原理(4)之then方法链式调用的初步实现 从零手撕Promise,掌握Promise的实现原理(5)之then方法链式调用的进阶实现