在上一步,我们已经实现了Promise的基本功能, 构造一个Promise对象,并且可以同步或者异步,将其status转变为 resolved
或者 rejected
,并且then方法能正确处理同步的状态变化,但是异步的状态变化我们处理不了。。
回忆一下我们上一步的then方法:
Promise3.prototype.then = function(onFulfilled, onRejected) {
const { error , value } = this
// 这里必须要做一个判断 如果是resolved才去调用onFulfilled, 如果是rejected去调用onRejected
if ( this.status === 'resolved' ) {
onFulfilled(value)
}
if ( this.status === 'rejected' ) {
onRejected(error)
}
}
因为我们在执行then函数的时候,此时由于是异步resolved,这个时候Promise对象的状态 并没有变为resolved
/ rejected
仍然是pending 。所以并不会进入两个if语句执行我们传递的onFulfilled函数和 onRejected函数。
所以我们需要一个对象存储onFulfilled函数,在resolve1函数执行之后去执行这个函数即可。但是请注意因为我们一个Promise对象,可以独立调用多次then函数,多次执行resolve之后的动作,所以我们给构造函数添加两个数组,一个为fulFillTasks
,一个为rejectTasks
, 存储传入的函数。
代码长这样:
function Promise4(executor) {
// 异步队列?
// 比如在构造函数里面是一个setTimeout,resolve是在下一个事件循环队列再去执行的
// 这种先后顺序的问题,一般都是放进一个任务队列,前一个执行了 再去触发后一个,参考lazyman
this.fulFillTasks = []
this.rejectTasks = []
this.status = "pending"
let value = null
let error = null
var resolve1 = (resolveValue) => {
if (this.status === "pending") {
this.status = "resolved"
this.value = resolveValue
this.fulFillTasks.forEach(fn => {
fn && fn(resolveValue)
})
}
}
var reject1 = (rejectError) => {
if (this.status === "pending") {
this.status = "rejected"
this.error = rejectError
this.rejectTasks.forEach(fn => {
fn && fn(rejectError)
})
}
}
executor(resolve1, reject1)
}
Promise4.prototype.then = function (onFulfilled, onRejected) {
const value = this.value
const error = this.error
//todo 判断 onFulfilled 和 onRejected 是函数
this.fulFillTasks.push(onFulfilled)
this.rejectTasks.push(onRejected)
if (this.status === 'resolved') {
onFulfilled(this.value)
}
if (this.status === 'rejected') {
onRejected(this.error)
}
}
这样就完成了这一步,做一个实验
var p = new Promise4((resolve, reject) => {
resolve('then invoke twice sync')
// setTimeout(() => {
// resolve('then invoke twice async')
// }, 3000)
})
p.then((v) => {
console.log('1111', v)
})
p.then(v => {
console.log('2222', v)
})
在异步resolve之后,才去执行then传递的函数,也做到了。。我们继续往下走
按照promise A+ 规范,或者我们自己看mdn上的then方法解释。
then 方法 需要返回一个promise,叫它小p,而且我们都知道在onFulfilled、onRejected回调函数里面返回的值,会作为小p的接收状态、拒绝状态的回调函数的参数值。如果没有返回值,那么就是默认返回了一个undefined,undefined会作为小p的接收状态的回调函数的参数值。【不熟悉的话,自己用原生的Promise做个实验就行了】 我们到这一步暂时考虑返回的是一个promise的情况。
我们还是分两种情况,第一种是立即resolve的promise对象的then的实现【构造函数里面同步调用resolve or reject ,另外就是已经是接受状态的then接受回调函数的实现】,一种是异步resolve的promise对象的then的实现。
我们修改then方法如下:
Promise4.prototype.then = function (onFulfilled, onRejected) {
// 有两种情况会执行下面的代码 A 和 B
// 1,构造函数里面传入的resolve 和 reject是同步代码块
// 2,promise已经是接收状态或者拒绝状态
// A 执行接收状态的回调函数
if (this.status === 'resolved') {
// const fulfillRet = onFulfilled(this.value)
// onFulfilled可能是在异步resolve后执行,在任务队列里面执行
// 所以我们这里将这个返回值定义到实例属性上
this.fulfillRet = onFulfilled(this.value)
return new Promise4((resolve, reject) => {
resolve(fulfillRet)
})
}
// B 执行拒绝状态的回调函数
if (this.status === 'rejected') {
// const rejectRet = onRejected(this.error)
this.rejectRet = onFulfilled(this.value)
return new Promise4((resolve, reject) =>{
reject(rejectRet)
})
}
// 构造函数里面传入的resolve 和 reject是在异步代码块里面执行
// 错误
// this.fulFillTasks.push(onFulfilled)
// this.rejectTasks.push(onRejected)
// 遇到了一些苦困难,这里因为是异步的,返回的Promise一开始的状态值肯定是pending
// 但是如何获取到异步的 onFulfilled的返回值,并且在resolve之后 将这个返回值
// 传递给返回的这个promise
// 正确
return new Promise4((resolve, reject) => {
// 非常关键,将回调函数和返回的promise的resove函数封装到一个函数里面
// 然后放到调用then方法的promise的任务队列里面
// 这样调用then方法的promise 状态从 pending 变为 resolved的时候
// then方法返回的promise,的状态才会从pending 变为 resolved
this.fulFillTasks.push((val) => {
let ret = onFulfilled(val)
resolve(ret)
})
this.rejectTasks.push((err) => {
let errRet = onRejected(err)
// 这里也是resolve
resolve(errRet)
})
})
}
到这一步,我们已经可以实现,新new出来的Promise对象异步调用resolve函数改变状态,这个时候then返回的Promise对象随着调用then的Promise对象变化而变化,并且获取到了then回调函数里面的值。
看起来已经完成了,实际上这里犯了一个关键的错误:then回调始终是异步调用的,但是我们的代码却没有体现。
我们拿Promise4 做一个测试,还是根据上面的测试代码来,加一句代码:
var p = new Promise4((resolve, reject) => {
resolve('then invoke twice sync')
// setTimeout(() => {
// resolve('then invoke twice async')
// }, 3000)
})
p.then((v) => {
console.log('first resolve callback', v)
})
p.then(v => {
console.log('second resolve callback', v)
})
console.log('sync code in main stack')
你会发现代码打印顺序会是
// first resolve callback
// second resolve callback
// sync code in main stack
实际上期望的应该是:
// sync code in main stack
// first resolve callback
// second resolve callback
所以我们要对代码修改,构造异步执行最好的办法就是setTimeout(fn, time)。
我们修改上面的then方法成下面的样子,构造函数不变:
Promise4.prototype.then = function (onFulfilled, onRejected) {
// 有两种情况会执行下面的代码 A 和 B
// 1,构造函数里面传入的resolve 和 reject是同步代码块
// 2,promise已经是接收状态或者拒绝状态
// A 执行接收状态的回调函数
if (this.status === 'resolved') {
// const fulfillRet = onFulfilled(this.value)
// onFulfilled可能是在异步resolve后执行,在任务队列里面执行
// 所以我们这里将这个返回值定义到实例属性上
setTimeout(() => {
this.fulfillRet = onFulfilled(this.value)
}, 0)
return new Promise4((resolve, reject) => {
setTimeout(() => {
resolve(this.fulfillRet)
}, 0)
})
}
// B 执行拒绝状态的回调函数
if (this.status === 'rejected') {
// const rejectRet = onRejected(this.error)
setTimeout(() => {
this.rejectRet = onFulfilled(this.value)
}, 0)
return new Promise4((resolve, reject) => {
setTimeout(() => {
reject(this.rejectRet)
}, 0)
})
}
// 构造函数里面传入的resolve 和 reject是在异步代码块里面执行
// 错误
// this.fulFillTasks.push(onFulfilled)
// this.rejectTasks.push(onRejected)
// 遇到了一些困难,这里因为是异步的,返回的Promise一开始的状态值肯定是pending
// 但是如何获取到异步的 onFulfilled的返回值,并且在resolve之后 将这个返回值
// 传递给返回的这个promise
// 正确
return new Promise4((resolve, reject) => {
// 非常关键,将回调函数和返回的promise的resove函数封装到一个函数里面
// 然后放到调用then方法的promise的任务队列里面
// 这样调用then方法的promise 状态从 pending 变为 resolved的时候
// then方法返回的promise,的状态才会从 pending 变为 resolved
// this.fulFillTasks.push((val) => {
// let ret = onFulfilled(val)
// resolve(ret)
// })
// this.rejectTasks.push((err) => {
// let errRet = onRejected(err)
// // 这里也是resolve
// resolve(errRet)
// })
var resolveFunc = (val) => {
return setTimeout(() => {
let ret = onFulfilled(val)
resolve(ret)
}, 0)
}
this.fulFillTasks.push(resolveFunc)
// 函数式编程
var rejectFunc = (err) => {
return setTimeout(() => {
let errRet = onRejected(err)
resolve(errRet)
}, 0)
}
this.rejectTasks.push(rejectFunc)
})
}
然后我们做一次实验:
var p = new Promise4((resolve, reject) => {
// resolve("kev")
setTimeout(()=> {
console.log("2s后执行resolve")
resolve('resolve')
}, 2000)
})
var y = p.then(()=>{
console.log("到resolve的回调函数里面了");
return "kevin"
}
)
console.log("1")
console.log("p.status", p.status)
console.log("y.status", y.status)
bingo! 完成了,这一节我们实现了 Promise构造函数里面异步调用resolve/reject,对象状态的变化可以根据异步resolve/reject来控制,异步执行resolve/reject之后,对象的状态才回从pending ------> resolved / rejected。
然后实现了then方法返回一个新的promise,并且这个Promise对象的状态控制也是和调用then方法的对象的状态关联的。
新的问题: 我们上面假定了一个操作是: then方法的接受状态回调onFulfilled和拒绝状态回调onRejected的返回值不是一个Promise对象,但是返回值 是作为then方法 返回的 Promise对象 小p的接收状态和拒绝状态的回调函数的参数值。
这种情况如何处理呢? 下篇文章再见。。。。