初始化构造函数
我们都知道Promise都有三种状态 pending,fulfilled,rejected,不仅有状态还有一个唯一的结果值,不管是fulfilled还是reject都会有值,而且参数(也就是我们传入的executor)会立即同步的执行,并给 executor 传入两个函数参数(resolve,reject),resolve和reject函数是用来改变Promise的状态的,很重要!!!
注意:executor 在执行的期间如果发生异常,会直接reject
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const PENDING = 'pending'
class MyPromise2 {
constructor(executor) {
this.state = PENDING
this.value = undefined
try {
// 绑定this,因为后面_resolve和_reject都会修改实例的状态,所以this需要是当前实例
executor(this._resolve.bind(this), this._resolve.bind(this))
} catch (error) {
this._resolve(error)
}
}
_resolve(data) {}
_reject(reason) {}
}
改变状态的函数
resolve和reject都有类似的行为,所以抽取一下函数
class MyPromise2 {
constructor(executor) {
this.state = PENDING
this.value = undefined
try {
executor(this._resolve.bind(this), this._resolve.bind(this))
} catch (error) {
this._resolve(error)
}
}
_changeState(state, value) {
this.state = state
this.value = value
}
_resolve(data) {
this._changeState(FULFILLED, data)
}
_reject(reason) {
this._changeState(REJECTED, reason)
}
}
then函数的初始化
我们得知道then到底是做什么的。首先肯定是返回一个新的Promise实例,因为then可以进行链式调用,然后是then函数里的两个状态的回调函数(onFulfilled,onRejected)什么时候执行。同一个Promise实例注册then的回调函数会只有一对吗。看以下代码:
const p = new MyPromise2((resolve, reject) => {
resolve(123)
})
p.then(console.log,console.log)
p.then(
function (data) {
console.log(data)
},
function (err) {
console.log(err)
}
)
上面代码可不是链式调用,是同一个Promise实例注册then的回调函数有多个。至少从现在看来,我们明确then的回调函数肯定会加入到一个执行任务的队列中。啥时候执行呢?肯定是状态已决时,也就是调用resolve或reject。 总结一下:
- 返回新的Promise实例
- 回调函数不会立即执行,而是状态已决时,也就是调用resolve或reject函数时
- 回调函数不一定只注册一对,所以肯定有一个执行的队列。
class MyPromise2 {
constructor(executor) {
//...
// 我这里是用一个数组来表示成功和失败回调函数的执行队列
// 当然也可以用两个数组,一个表示成功的队列,一个表示失败的队列
this.handlers = []
// ...
}
// ...
_pushHandler(executor, state) {
this.handlers.push({
executor,
state,
})
}
then(onFulfilled, onRejected) {
return new MyPromise2((resolve, reject) => {
// 因为我上面只用一个数组来表示执行队列,所以需要加一个状态,用来区分当前函数是成功时执行还是失败时执行
this._pushHandler(onFulfilled, FULFILLED)
this._pushHandler(onRejected, REJECTED)
})
}
}
then函数的链式调用
我们上面已经返回一个新的实例了,是不是说已经实现了链式调用呢?不是,还有一点就是后面的then函数的执行队列啥时候执行呢?看以下代码:
const p = new MyPromise2((resolve, reject) => {
// setTimeout(() => {
resolve(123)
// })
})
const p2 = p.then(
function A1(data) {
console.log(data)
},
function B1(err) {
console.log(err)
}
)
p2.then(console.log, console.log)
p2里面的handlers啥时候执行呢,then函数肯定会把回填函数加入handlers。执行一定是状态发生了改变,那么P2的状态由谁来改变呢?按照规范,是由p的状态下执行的回调函数来决定,也就是A1和B1其中一个。 简单来说,假设p的状态是fulfill1ed,那个p2的状态就由A1函数决定,如果A1在执行过程中,没有发生异常,那么p2的状态也是fulfill1ed的,也就会执行p2的成功的队列,反之依然。这就是链式调用。 所以我们在_pushHandler的时候还要把then返回的新实例的 resolve 和 reject加入进去,方便我们执行A1或B1的时候可以调用 当前then函数返回新实例的resolve或reject,改变p2的状态。
_pushHandler(executor, state, resolve, reject) {
this.handlers.push({
executor,
state,
resolve,
reject,
})
}
then(onFulfilled, onRejected) {
return new MyPromise2((resolve, reject) => {
this._pushHandler(onFulfilled, FULFILLED, resolve, reject)
this._pushHandler(onRejected, REJECTED, resolve, reject)
})
}
队列的执行Timing
执行时机发生在状态改变时,也就是我们的_changeState函数,还有就是then函数执行时
_changeState(state, value) {
this.state = state
this.value = value
// 加入运行handlers的逻辑
}
_runHandlers() {
if (this.state === PENDING) return
while (this.handlers.length) {
// 取出队列首的任务
this._runOneHandler(this.handlers.shift())
}
}
_runOneHandler({ executor, state, resolve, reject }) {
if (this.state !== state) return
// 放到微任务队列去执行
queueMicrotask(() => {
// 不是函数,那么then返回的promise的状态就和当前promise的状态保持一致
if (typeof executor !== 'function') {
this.state === FULFILLED ? resolve(this.value) : reject(this.value)
return
}
try {
const result = executor(this.value)
// 如果是回调函数返回的是一个Promise
if (result instanceof MyPromise) {
// then函数返回的Promise的状态由他决定
result.then(resolve, reject)
return
}
// 正常走逻辑
resolve(result)
} catch (error) {
reject(error)
}
})
}
then(onFulfilled, onRejected) {
return new MyPromise2((resolve, reject) => {
this._pushHandler(onFulfilled, FULFILLED, resolve, reject)
this._pushHandler(onRejected, REJECTED, resolve, reject)
this._runHandlers()
})
}