实现promise要在熟练使用的基础上完成,首先要明确几点
- promise是一个类
- promise有三种状态:成功(fulfilled)、失败(rejected)、等待(pending)
- pending -> fulfilled / rejected
- 状态一旦确定就不可以再改变
- resolve和reject函数是用来改变状态的
- resolve: fulfilled
- reject: rejected
- 调用then方式传入两个参数,第一个参数为状态fulfilled时的回调函数,第二个参数为状态为rejected时的回调函数
第一版 – 基础核心原理的实现
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING // promise状态
value = undefined // 成功时的值 then方法需要返回
error = undefined // 失败时的值 then方法需要返回
// 成功时的函数
reslove = value => {
// 修改promise状态
this.status = FULFILLED
this.value = value
}
// 失败时的函数
reject = error => {
// 修改promise状态
this.status = REJECTED
this.error = error
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallback(this.error)
break
}
}
}
// test
const promise = new MyPromise((reslove, reject) => {
reslove('reslove')
})
promise.then(value => {
console.log(value) // reslove
}, error => {
console.log(error)
})
复制代码
通过上面的代码,我们成功实现了对promise基础原理。调用promise.then方法可以成功打印出‘reslove’。但是,在then方法的实现中我们并没有对PENDING状态做处理。也就是说,假如我们进行异步操作,当前的promise是无法处理的。比如:
const promise = new MyPromise((reslove, reject) => {
setTimeout(()=> {
reslove('reslove')
},2000)
})
复制代码
第二版 – 处理异步情况
那么异步情况如何处理呢?以上面setTimeout为例,reslove是在2秒后执行,在调用then方法2秒内当前promise的状态仍然为PENDING。
两步解决:
- PENDING状态时,对成功or失败回调作暂存
- 在reslove/reject 函数中判断是否有回调函数,有则执行
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING // promise状态
value = undefined // 成功时的值 then方法需要返回
error = undefined // 失败时的值 then方法需要返回
successCallback = undefined // 成功回调
failCallback = undefined // 失败回调
// 成功时的函数
reslove = value => {
// 修改promise状态
this.status = FULFILLED
this.value = value
this.successCallback && this.successCallback(this.value)
}
// 失败时的函数
reject = error => {
// 修改promise状态
this.status = REJECTED
this.error = error
this.failCallback && this.failCallback(this.error)
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallBack(this.error)
break
// 异步情况处理
case PENDING:
this.successCallback = successCallback
this.failCallback = failCallback
break
}
}
}
// test
const promise = new MyPromise((reslove, reject) => {
setTimeout(()=> {
reslove('reslove')
}, 2000)
})
promise.then(value => {
console.log(value) // 2秒后: reslove
}, error => {
console.log(error)
})
复制代码
实现第二版后,问题又来了。我们平时使用promise时同一个promise下的then方法是可以多次调用的。我们将测试代码改为
// test
const promise = new MyPromise((reslove, reject) => {
setTimeout(()=> {
reslove('reslove')
}, 2000)
})
promise.then(value => {
console.log('第一次调用then: ', value)
})
promise.then(value => {
console.log('第二次调用then: ', value)
})
// 第二次调用then reslove
复制代码
发现代码只执行了最后一次then方法,打印“第二次调用then: reslove”。这显然是与我们的预期不相符的
第三版 – 多次调用
要实现多次调用,我们需要做的是: 以数组形式暂存多个callback,并且在reslove/reject的时候依次调用
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING // promise状态
value = undefined // 成功时的值 then方法需要返回
error = undefined // 失败时的值 then方法需要返回
// 数组暂存
successCallback = [] // 成功回调
failCallback = [] // 失败回调
// 成功时的函数
reslove = value => {
// 修改promise状态
this.status = FULFILLED
this.value = value
// 依次调用
while(this.successCallback.length) this.successCallback.shift()(this.value)
}
// 失败时的函数
reject = error => {
// 修改promise状态
this.status = REJECTED
this.error = error
// 依次调用
while(this.failCallback.length) this.failCallback.shift()(this.error)
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallBack(this.error)
break
// 异步情况处理
case PENDING:
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
break
}
}
}
// test
const promise = new MyPromise((reslove, reject) => {
setTimeout(()=> {
reslove('reslove')
}, 2000)
})
promise.then(value => {
console.log('第一次调用then: ', value)
})
promise.then(value => {
console.log('第二次调用then: ', value)
})
promise.then(value => {
console.log('第三次调用then: ', value)
})
// 第一次调用then: reslove
// 第二次调用then: reslove
// 第三次调用then: reslove
复制代码
除了多次调用外,promise还支持链式调用。并且后面then方法的回调函数拿到的值是上一个then方法的回调函数的返回值。
第四版 – 链式调用
这里值得注意的是,我们需要判断上一个then的返回值是什么类型。如果是普通的值,可以直接调用reslove方法。如果是promise对象,则需要根据promise对象返回的结果来决定调用reslove或者reject。
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING // promise状态
value = undefined // 成功时的值 then方法需要返回
error = undefined // 失败时的值 then方法需要返回
successCallback = [] // 成功回调
failCallback = [] // 失败回调
// 成功时的函数
reslove = value => {
// 修改promise状态
this.status = FULFILLED
this.value = value
while(this.successCallback.length) this.successCallback.shift()(this.value)
}
// 失败时的函数
reject = error => {
// 修改promise状态
this.status = REJECTED
this.error = error
while(this.failCallback.length) this.failCallback.shift()(this.error)
}
then(successCallback, failCallback) {
return new Promise((reslove, reject) => {
switch (this.status) {
case FULFILLED:
reslovePromise(successCallback(this.value), reslove, reject)
break
case REJECTED:
failCallBack(this.error)
break
// 异步情况处理
case PENDING:
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
break
}
})
}
}
// 通用方法 - 解析promise对象 (PENDING、 FULFILLED、REJECTED都会用到)
function reslovePromise(x, reslove, reject) {
if (x instanceof MyPromise) {
x.then(reslove, reject)
} else {
reslove(x)
}
}
// test
const promise = new MyPromise((reslove, reject) => {
reslove('reslove')
})
promise.then(value => {
console.log('第一次调用then: ', value)
return 'reslove2'
}).then(value => {
console.log('第二次调用then: ', value)
return new MyPromise((reslove, reject) => {
reslove('reslove3')
})
}).then(value => {
console.log('第三次调用then: ', value)
})
// 第一次调用then: reslove
// 第二次调用then: reslove2
// 第三次调用then: reslove3
复制代码
到第四版,我们的MyPromise实现了promise的基本功能。但从代码可以看出,我们并未对MyPromise做任何错误处理,这并不是一段健壮的代码该有的样子。那么接下来,我们就对MyPromise做一些代码优化及错误处理。
第五版 – 代码优化及错误处理
错误处理想到了两方面。一是在reslove方法执行报错时,将状态改为REJECTED,执行reject方法。二是识别promise返回自对象。
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
try {
exector(this.reslove, this.reject)
} catch(e) {
this.reject(e)
}
}
status = PENDING // promise状态
value = undefined // 成功时的值 then方法需要返回
error = undefined // 失败时的值 then方法需要返回
successCallback = [] // 成功回调
failCallback = [] // 失败回调
// 成功时的函数
reslove = value => {
// 修改promise状态
this.status = FULFILLED
this.value = value
while(this.successCallback.length) this.successCallback.shift()()
}
// 失败时的函数
reject = error => {
// 修改promise状态
this.status = REJECTED
this.error = error
while(this.failCallback.length) this.failCallback.shift()()
}
then(successCallback, failCallback) {
// 将then方法的参数变为可选参数
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : error => {throw error}
let newPromise = new MyPromise((reslove, reject) => {
switch (this.status) {
case FULFILLED:
setTimeout(()=> {
try {
reslovePromise(newPromise, successCallback(this.value), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
break
case REJECTED:
setTimeout(()=> {
try {
reslovePromise(newPromise, failCallback(this.error), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
break
// 异步情况处理
case PENDING:
this.successCallback.push(()=> {
setTimeout(()=> {
try {
reslovePromise(newPromise, successCallback(this.value), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
})
this.failCallback.push(()=> {
setTimeout(()=> {
try {
reslovePromise(newPromise, failCallback(this.error), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
})
break
}
})
return newPromise
}
}
// 通用方法 - 解析promose对象 (PENDING、 FULFILLED、REJECTED都会用到)
function reslovePromise(newPromise ,x, reslove, reject) {
if (newPromise === x) {
return reject(new TypeError('循环调用'))
}
if (x instanceof MyPromise) {
x.then(reslove, reject)
} else {
reslove(x)
}
}
// test
const promise = new MyPromise((reslove, reject) => {
setTimeout(()=> {
reslove('reslove')
}, 2000)
// reject('reject')
})
promise.then(value => {
console.log('第一次调用then reslove: ', value)
throw new Error('then error')
}, error => {
console.log('第一次调用then reject: ', error)
}).then(value => {
console.log('第二次调用then reslove: ', value)
}, error => {
console.log('第二次调用then reject: ', error)
})
// 第一次调用then reslove: reslove
// 第二次调用then reject: Error: then error
复制代码