一、前言
不折腾不前端,在现公司待了3年了,每一段时间都会去不同的公司面试,目的呢一是为了看下市场的动态,对前端有什么要求。二是为了测量自己在前端市场上能值多少钱。三是为了巩固知识,提升面试技巧。
当然,不能挑那些你想要进的公司去做实验,像福厂、猪场等,他们会有你面试记录,影响之后的面试,得不偿失。
最近在面试过程中,被问到了一个老生常谈的题目,promise的实现原理。所以决定写一篇关于promise源码实现的文章。
二、promsie使用方式
正常在实现某个api源码的时候,首先是能熟练使用这个api,从api上考虑如何去实现这个源码,这是我一直以来的方式。
new MyPromise((resolve, reject) => {
}).then((data) => {
// do something
}).then((data) => {
// do something
}).catch((err) => {
// do something
})
三、源码实现
非常简单的一个例子,可以看出promise是一个构造函数,有几个方法,resolve, reject, then,catch,resolve和reject改变状态,根据状态执行then或者catch,所以可以马上就写出promise的接结构
Class Promise {
constructor (executor) {
this.status = 'pedding' // pedding fulfilled reject, 初始状态pedding
this.value = null // resolve结果
this.reason = null // reject原因
// 专门存放成功的回调函数
this.onResolvedCallbacks = [];
// 专门存放成功的回调函数
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status !== 'pedding') {
return
}
this.status = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn(this.value))
}
let reject = (reason) => {
if (this.status !== 'pedding') {
return
}
this.status = 'reject'
this.reason = reason
this.onResolvedCallbacks.forEach(fn => fn(this.value))
}
try {
executor(resolve, reject) // 同步执行
} catch(err) {
reject()
}
}
then (onfulfilled, onrejected) {
// 使用setTimeout模拟异步
if (this.status === 'pedding') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
onfulfilled(this.value)
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onrejected(this.value)
}, 0)
})
} else if (this.status === 'fulfilled') {
setTimeout(() => {
onfulfilled(this.value)
}, 0)
} else {
setTimeout(() => {
onrejected(this.value)
}, 0)
}
}
catch (errHander) {
this.then(null, errHander)
}
}
一个简易的promise就完成了,但是运行时发现并不能链式调用,原因就在于then的返回值不是一个promise,没有then方法,所以报错。所以处理方法就是返回一个新的promise,那么有一个问题,为什么是新的promise而不是return this?
四、链式调用
因为为了保证逻辑的简单性,promise的状态只能从pedding变为fulfilled或者reject,这也是promise名字的由来。如果使用return this,且then回调函数执行报错,状态还是fulfilled,promise是无法catch到错误。 那么我们改一下代码
then (onfulfilled, onrejected) {
let newPromise = new MyPromise((resolve, reject) => {
if (this.status === 'pedding') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
onfulfilled(this.value)
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onrejected(this.reason)
}, 0)
})
} else if (this.status === 'fulfilled') {
setTimeout(() => {
let value = onfulfilled(this.value)
reject(value)
}, 0)
} else {
setTimeout(() => {
let reason = onrejected(this.reason)
reject(reason)
}, 0)
}
})
return newPromise
}
至此,一个简易的promise就实现了,包含了then,catch方法,面试够用。
五、优化
细心的同学可能已经发现,如果onfulfilled如果返回的是一个promise呢?我们不希望then拿到的结果是一个promise,那么就要等这个promise resolve时才能继续执行then方法。所以我们再改造一下
// 添加一个函数
function resolvePromise(promise, x, resolve, reject) {
if ((typeof x === 'object' && x != null && x._proto_.contructor === MyPromse.contructor)) {
// 此时就认为它是一个promise
let then = x.then
then.call(x, y => { // 从then中能拿到该x的结果,再resolve出去
resolvePromsie(promise, y, resolve, reject) // 一直递归
}, r => {
reject(r);
})
} else if(typeof x === 'function') {
let value = x()
resolvePromise(promise, value, resolve, reject)
} else {
resolve(x)
}
}
// 改造下then方法
then (onfulfilled, onrejected) {
let newPromise = new MyPromise((resolve, reject) => {
if (this.status === 'pedding') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
let x = onfulfilled(this.value)
resolvePromsie(newPromse, x, resolve, reject)
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
let x = onrejected(this.reason)
resolvePromsie(newPromse, x, resolve, reject)
}, 0)
})
} else if (this.status === 'fulfilled') {
setTimeout(() => {
let value = onfulfilled(this.value)
resolvePromsie(newPromse, value, resolve, reject)
}, 0)
} else {
setTimeout(() => {
let reason = onrejected(this.reason)
resolvePromsie(newPromse, reason, resolve, reject)
}, 0)
}
})
return newPromise
}
六、总结
大多数时候,我们再日常工作中是不用去写promise源码,但是为了不被外界看成“熟练的api打工人”,还是撸一撸源码,其次也是为了promise的设计思路,提升编程能力。更重要的是基本每个面试官都会问,虽然不知道他们自己会不会,但是准备着总是没错的。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!