自己实现一个Promise

序言

Promise是ES6提出的异步编程规范。接下来我们一步一步来探究下Promise的实现原理,根据Promise A+规范实现一个我们自己的Promise。

Promise的初步实现

Promise的简单使用

new Promise((resolve, reject) => {
  resolve('a')
}).then(value => {
  console.log(value)
}, reason => {
  console.log(reason)
})

Promise有三种状态: 等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。

  • 等待态(Pending)
    处于等待态时,promise 需满足以下条件:
    • 可以迁移至执行态或拒绝态
  • 执行态(Fulfilled)
    处于执行态时,promise 需满足以下条件:
    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的终值
  • 拒绝态(Rejected)
    处于拒绝态时,promise 需满足以下条件:
    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的据因

同时promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected)

onFufilled是promise状态为Fulfilled时的回调,onRejected是promise状态为Rejected时的回调。
根据以上的基本要求,我们可以实现一个得到Promise的简单实现

const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(executor) {
    // excutor必须是函数
    if (typeof executor !== 'function') {
      throw new TypeError('Promise resolver a is not a function')
    }
    // 状态
    this.status = PENDING
    // fullfilled时接受的参数
    this.value = null
    // rejected的原因
    this.reason = null
    this.fullfilledCallback = []
    this.rejectCallback = []
    try {
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value
      this.status = FULLFILLED
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
    }
  }
  then(onFullfilled, onRejected) {
    // 如果onFullfilled和onRejected不是函数,则将其包装成函数
    if (typeof onFullfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    if (typeof onRejected !== 'function') {
      onRejected = function(reason) {
        throw reason
      }
    }

    if (this.status === FULLFILLED) {
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }
}

Promise异步解决方案实现

我们知道Promise时的then中的回调函数时异步执行的,比如下面这段代码

console.log(1)
new Promise((resolve, reject) => {
  console.log(2)
  resolve('a')
}).then(value => {
  console.log(3)
  console.log(value)
}, reason => {
  console.log(reason)
})

console.log(4)

输入为1, 2, 4, 3,所以我们需要将then中的回调变为异步执行

 then(onFullfilled, onRejected) {
   // 如果onFullfilled和onRejected不是函数,则将其包装成函数
   if (typeof onFullfilled !== 'function') {
     onFulfilled = function (value) {
       return value
     }
   }

   if (typeof onRejected !== 'function') {
     onRejected = function(reason) {
       throw reason
     }
   }

   if (this.status === FULLFILLED) {
     setTimeout(() => {  
       onFulfilled(this.value)
     }, 0);
   }
   if (this.status === REJECTED) {
     setTimeout(() => {
       onRejected(this.reason)
     }, 0);
   }
 }

在这里还有一个问题,如果我们传入的executor有异步逻辑怎么办,比如

console.log(1)
new MyPromise((resolve, reject) => {
  console.log(2)
  setTimeout(() => {
    resolve('haha')
  })
}).then(value => {
  console.log(3)
  console.log(value)
}, reason => {
  console.log(reason)
})

console.log(4)

按照刚刚完成的代码,then方法的执行早于resolve方法的执行。在这种情况下,then方法执行时,Promise的状态为pending。这时候,我们需要将onFulfiiledonReject函数保存下来,当Promise状态改变为fulfilled或者rejected时再调用回调函数,所以

const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(executor) {
    // excutor必须是函数
    if (typeof executor !== 'function') {
      throw new TypeError('Promise resolver a is not a function')
    }
    // 状态
    this.status = PENDING
    // fullfilled时接受的参数
    this.value = null
    // rejected的原因
    this.reason = null
    this.fullfilledCallback = []
    this.rejectCallback = []
    try {
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      // 为了能捕获
      this.reject(error)
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value
      this.status = FULLFILLED
      this.fullfilledCallback.forEach(fn => {
        fn(value)
      })
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
      this.rejectCallback.forEach(fn => {
        fn(reason)
      })
    }
  }
  then(onFullfilled, onRejected) {
    // 如果onFullfilled和onRejected不是函数,则将其包装成函数
    if (typeof onFullfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    if (typeof onRejected !== 'function') {
      onRejected = function(reason) {
        throw reason
      }
    }
    if (this.status === FULLFILLED) {
      setTimeout(() => {  
         onFulfilled(this.value)
      }, 0);
    }
    if (this.status === REJECTED) {
      setTimeout(() => {
         onRejected(this.reason)
      }, 0);
    }
    if (this.status === PENDING) {
      this.fullfilledCallback.push(value => {
        setTimeout(() => {
          const x = onFulfilled(this.value)
        })
      })
      this.rejectCallback.push(reason => {
        setTimeout(() => {
          onRejected(this.reason)
        })
      })
    }
  }
}
Promise 链式调用的实现

Promise then方法必须返回了以新的Promise,这样就同时实现了Promise的链式调用,既然返回一个新的Promise的,那么我们在then方法中关于状态判断的执行逻辑放哪里?简单:只需放在新创建的Promise的executor函数中就行

then(onFullfilled, onRejected) {
    // 如果onFullfilled和onRejected不是函数,则将其包装成函数
    if (typeof onFullfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    if (typeof onRejected !== 'function') {
      onRejected = function(reason) {
        throw reason
      }
    }
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULLFILLED) {
        setTimeout(() => {  
          try {
            const x = onFulfilled(this.value)
            resolve(x)
          } catch (error) {
            reject(error)
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            resolve(x)
          } catch (e) {
            reject(e)
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.fullfilledCallback.push(value => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              resolve(x)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectCallback.push(reason => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              resolve(x)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }

在这里又有了一个问题,我们的Promise,如果onFulfiilled的返回值是基本数据类型是可以正常链式调用的。但如果onFulfilled返回一个新的Promise会怎样呢。我们可以使用下面的代码用ES6的Promise和我们自己的Promise测试以下

new Promise((resolve, reject) => {
  resolve('haha')
}).then(value => {
  console.log(value)
  return new Promise(() => {
    resolve("hehe")
  })
}, reason => {
  console.log(reason)
}).then(val => {
  console.log(val)
})

从测试结果我们可以很容易的看出我们的Promise是不符合规范的。该怎么办呢?

Promise 链式调用的最终解决方法

Promise A+ 对onFulfilledonRejected的返回值x的处理做了明确的规定:
如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程
Promise解决过程:
Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。

这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

运行 [[Resolve]](promise, x) 需遵循以下步骤:

  • x 与 promise 相等
    如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

  • x 为 Promise
    如果 x 为 Promise ,则使 promise 接受 x 的状态:

    • 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
    • 如果 x 处于执行态,用相同的值执行 promise
    • 如果 x 处于拒绝态,用相同的据因拒绝 promise
  • x 为对象或函数
    如果 x 为对象或者函数:

    • 把 x.then 赋值给 then 注5
    • 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
    • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
      • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
      • 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
      • 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
      • 如果调用 then 方法抛出了异常 e:
        • 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        • 否则以 e 为据因拒绝 promise
      • 如果 then 不是函数,以 x 为参数执行 promise
  • 如果 x 不为对象或者函数,以 x 为参数执行 promise

本质上Promise解决方法就是一个函数,用来处理onFulfilled或者onRejected的返回值x,下面我们来实现这个函数

MyPromise.resovePromise = (x, promise, resovle, reject) => {
  // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (x === promise) {
    reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
  }
  // 如果x是promise,等待x执行完毕
  if (x instanceof MyPromise) {
    // 等待x执行完毕
    x.then(value=> {
      // value 也有可能是一个Promise或者对象或者函数
      MyPromise.resovePromise(value, promise, resovle, reject)
    }, reason => {
      reject(reason)
    }) 
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    const then = x.then
    // 如果x拥有then是个函数

    // 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用, 所以我们表明一个变量是否被调用过
    let call = false
    try {
      if (typeof then === 'function') {
        then.call(x, value => {
          if (call) return
          call = true
          MyPromise.resovePromise(value, promise, resovle, reject)
        }, reason => {
          if (call) return
          call = true
          reject(reason)
        })
      } else {
        if (call) return
        call = true
        resolve(x)
      }
    } catch(e) {
      if (call) return
      call = true
      reject(e)
    }
  } else {
    try {
      resolve(x)
    } catch (e) {
      reject(e)
    }
  }
}

之后我们对onFulfiiled和onReject的返回值应用这个函数就可以

let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULLFILLED) {
        setTimeout(() => {  
          try {
            const x = onFulfilled(this.value)
            MyPromise.resovePromise(x, promise2, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            MyPromise.resovePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.fullfilledCallback.push(value => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              MyPromise.resovePromise(x, promise2, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectCallback.push(reason => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              MyPromise.resovePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })

至此,我们的Promise就完成了

完成代码
const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(executor) {
    // excutor必须是函数
    if (typeof executor !== 'function') {
      throw new TypeError('Promise resolver a is not a function')
    }
    // 状态
    this.status = PENDING
    // fullfilled时接受的参数
    this.value = null
    // rejected的原因
    this.reason = null
    this.fullfilledCallback = []
    this.rejectCallback = []
    try {
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      // 为了能捕获
      this.reject(error)
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value
      this.status = FULLFILLED
      this.fullfilledCallback.forEach(fn => {
        fn(value)
      })
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
      this.rejectCallback.forEach(fn => {
        fn(reason)
      })
    }
  }
  then(onFullfilled, onRejected) {
    // 如果onFullfilled和onRejected不是函数,则将其包装成函数
    if (typeof onFullfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    if (typeof onRejected !== 'function') {
      onRejected = function(reason) {
        throw reason
      }
    }
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULLFILLED) {
        setTimeout(() => {  
          try {
            const x = onFulfilled(this.value)
            MyPromise.resovePromise(x, promise2, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            MyPromise.resovePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.fullfilledCallback.push(value => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              MyPromise.resovePromise(x, promise2, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectCallback.push(reason => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              MyPromise.resovePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
}

MyPromise.resovePromise = (x, promise, resovle, reject) => {
  // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (x === promise) {
    reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
  }
  // 如果x是promise,等待x执行完毕
  if (x instanceof MyPromise) {
    // 等待x执行完毕
    x.then(value=> {
      // value 也有可能是一个Promise或者对象或者函数
      MyPromise.resovePromise(value, promise, resovle, reject)
    }, reason => {
      reject(reason)
    }) 
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    const then = x.then
    // 如果x拥有then是个函数

    // 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用, 所以我们表明一个变量是否被调用过
    let call = false
    try {
      if (typeof then === 'function') {
        then.call(x, value => {
          if (call) return
          call = true
          MyPromise.resovePromise(value, promise, resovle, reject)
        }, reason => {
          if (call) return
          call = true
          reject(reason)
        })
      } else {
        if (call) return
        call = true
        resolve(x)
      }
    } catch(e) {
      if (call) return
      call = true
      reject(e)
    }
  } else {
    try {
      resolve(x)
    } catch (e) {
      reject(e)
    }
  }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值