今天来玩一下Promise,一步步分析。
- 原版Promise效果
let promise = new Promise((resolve, reject) => resolve("success"));
promise.then(msg => console.log(msg));
- 自定义一个Pomise
class MyPromise {
constructor(func) {
this.callbacks = []
// 绑定this指向
const resolve = this.resolve.bind(this)
func(resolve)
}
resolve(e) {
this.callbacks.forEach(cb => cb(e))
}
reject() {
console.error('reject')
}
then(cb) {
this.callbacks.push(cb)
}
}
new MyPromise((resolve, _) => setTimeout(() => resolve('success'), 0)).then(e => console.log(e))
-
执行结果
此时看起来似乎实现了功能,但是当
new MyPromise
的时候把异步
方法变成同步
方法,此时then
方法里无法打印出值,如:
new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e))
- 此时应该想办法把
then
方法里的函数放在resolve
方法执行之前,所以采取浏览器事件触发线程,把事件添加到待处理JS任务队列的队尾,等待js引擎的处理,让resolve
执行的时时候then
方法的回调函数已经存在回调数组callbacks
中,如setTimeout方法。这里就有人疑问了,不是刚去掉吗,怎么又加上哈哈。。。的确,但是加的位置不一样了。不过这里加的是微任务queueMicrotask,和setTimeout宏任务比起来微任务执行优先级更高,如:
class MyPromise {
constructor(func) {
this.callbacks = []
// 绑定this指向
const resolve = this.resolve.bind(this)
// 此处加上queueMicrotask微任务方法
queueMicrotask(() => func(resolve))
}
resolve(e) {
this.callbacks.forEach(cb => cb(e))
}
reject() {
console.error('reject')
}
then(cb) {
this.callbacks.push(cb)
}
}
new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e))
- 熟悉的结果回来了。要是执行中途出现异常,那就捕获异常并返回
reject
,如:
try {
queueMicrotask(() => func(resolve))
} catch (error) {
this.reject()
}
- 接着有人问,这里只有一个
then
方法,那么多个then
方法怎么办呢?好办,链式调用,在then
方法中return this
,如:
then(cb) {
this.callbacks.push(cb)
// 链式调用
return this
}
- 输出代码
new MyPromise((resolve, _) => resolve('success')).then(e => console.log(e)).then(e => console.log(e))
-
执行结果
-
这样看着可以执行多个
then
方法了,可是还有问题,细心的小伙伴发现了then
方法执行后的结果每次都是一样的,并没有随着上次执行的变化而变化,如:
两次
then
方法中取到的数据都是1
。所以要把then
方法返回的值保存一下
class MyPromise {
constructor(func) {
// 初始化值
this.value = null;
this.callbacks = []
// 绑定this指向
const resolve = this.resolve.bind(this)
queueMicrotask(() => func(resolve))
}
resolve(e) {
this.value = e
this.callbacks.forEach(cb => {
let newValue = cb(this.value)
// 把then方法返回的值存起来
this.value = newValue
})
}
reject() {
console.error('reject')
}
then(cb) {
this.callbacks.push(cb)
return this
}
}
-
执行结果
-
然后还有小伙伴说,当最后一次
then
方法是个耗时好操作怎么办,只会输出一个数值,如:
这里遵循Promise源码的规定,在Promise源码中是有状态这一说的,有
三
种状态,pending - 进行中
、fulfilled - 成功
、rejected - 失败
。当主体执行完后,把状态变更一下,后续再执行直接把后续加上的then
方式执行即可,所以实现一下,如:
// 状态对象
const stateObj = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
constructor(func) {
// 初始化状态
this.state = stateObj.PENDING
// 初始化值
this.value = null;
this.callbacks = []
// 绑定this指向
const resolve = this.resolve.bind(this)
try {
queueMicrotask(() => func(resolve))
} catch (error) {
this.reject()
}
}
resolve(e) {
this.value = e
// 改变状态
this.state = stateObj.FULFILLED
this.callbacks.forEach(cb => {
let newValue = cb(this.value)
// 把then方法返回的值存起来
this.value = newValue
})
}
reject() {
console.error(stateObj.REJECTED)
}
then(cb) {
if (this.state === stateObj.PENDING) {
this.callbacks.push(cb)
return this
}
try {
// 后续调用直接执行传进来的方法
cb(this.value)
} catch (error) {
this.reject()
}
return this
}
}
const mp = new MyPromise((resolve, _) => resolve(2)).then(e => {
console.log(e)
return e * 2
})
setTimeout(() => {
mp.then(e => {
console.log(e)
return e * 2
})
}, 3000)
-
执行结果又回来了
好咧,今天就玩到这里,后续继续优化