[聊一聊]Promise以及Promise.then的实现原理
大家好我是王大傻,最近呢,在工(mo)作(yu)群闲聊之际,发现有些朋友对于Promise的实现以及then方法的实现并不是很清晰,所以想借着这次机会,刚好手写实现下Promise
那么在正式学习前呢,咱们先来一起唠唠EvenLoop事件轮询机制。(图中所使用的分析网站地址已经放在文章最后了)
分析
首先,如图所示,我们执行事件中,无非于两种
- 同步任务
- 异步任务 同步任务自然不用多说,按照顺序,当前调用栈依次执行,如:
function foo1(){
console.log(2)
}
new Promise((res,rej)=>{
setTimeout(()=>{
res(1)
},3000)
}).then(res=>{
console.log(res)
})
foo1()
在上述函数中,我们首先执行的就是Promise(Promise实例阶段就是同步任务),所以也就有了Promise.resolve为同步任务自此,我们先执行了Promise,但是,此时Promise的内部出现了一个定时器,怎么办呢。。我们想想想,定时器是一个宏任务,所以它暂时会被推入宏任务队列,代码继续向下执行,由于没有更改Promise的状态所以此时的then还未执行,只是将其挂入到Promise内部的调用队列里面。代码自然来到了foo1部分,控制台打印2,至此,我们同步任务已经完成了,然后开始异步任务,异步任务执行顺序微任务->宏任务,我们先来到微任务队列,并没有执行的所以开始执行宏任务中的定时器,此时我们resolve了1,并且将微任务Promise.then推入了微任务队列,此时进行检查后,我们执行微任务,打印出来1,至此,任务一轮走完了,那么假如我们这样修改呢
function foo1(){
console.log(2)
}
new Promise((res,rej)=>{
setTimeout(()=>{
res(1)
},3000)
}).then(res=>{
setTimeout(()=>{
console.log(res)
return res
},100)
}).then(res=>{
console.log(res)
})
foo1()
此时的结果又是什么呢?211? 嗯,显然不是,代码是考不上211的,开句玩笑
那么正确结果是什么呢?这里就不卖关子了,大家听我分析
首先我们第一次输出2,这个是没问题的,毕竟是同步任务,但是在挂起Promise里面第一个定时器后,我们代码执行还是依次,此时先回去挂起第一个微任务但是发现里面有定时器,所以重复上述逻辑,将定时器推入到宏任务,接着将下一个微任务也去挂起,然后执行完我们第一个定时器后返回了值为1,那么此时第一个then函数和第二个then函数究竟先调用哪个呢,因为我们第一个里面有定时器,所以定义的是第二个then,而又由于then取得的res值是由前面的函数传递过来的,此时第一个then函数并没有传递任何值,所以打印undefined,最后执行我们的第一个then函数,输出1,所以答案是 2 undefined 1
简单了解了上述原理后,我们来实现下Promise吧 话不多说先来分析一波
分析
- Promise三种状态 reject pending resolve
- 值的储存 value
- 成功失败函数的储存
- 静态方法以及内部方法
- 返回的实例 包含两个参数 是两个函数 那么根据上述我们先实现下Promise
const PENDING = 'pending'
const RESOLVE = 'resolve'
const REJECT = 'reject'
class MyPromise {
// 先做初始化
status = PENDING // 初始化状态
value = undefined // 定义初始值
onSuccessQueue = [] // 成功函数任务队列
onFailedQueue = [] // 失败函数任务队列
constructor(exe) {
const resolve = (value) => {
const doFn = () => {
// 确保有且只有一次状态改变
if (this.status !== PENDING) return
this.value = value
this.status = RESOLVE
while (this.onSuccessQueue.length) {
this.onSuccessQueue.shift()()
}
}
// 模拟异步
setTimeout(doFn, 0)
}
const reject = (value) => {
const doFn = () => {
// 确保有且只有一次状态改变
if (this.status !== PENDING) return
this.value = value
this.status = REJECT
while (this.onFailedQueue.length) {
this.onFailedQueue.shift()()
}
}
// 模拟异步
setTimeout(doFn, 0)
}
try {
exe(resolve, reject)
} catch (e) {
reject(e)
}
}
}
我们在实现过程中通过定时器去模拟了异步的操作,但其实异步并不是微任务,所以和真正的Promise比起来还是有差距的,在此我们仅去了解原理即可
实现完了Promise,那我们趁热打铁继续来实现下Promise.then吧
Promise.then实现分析
- then函数中也是返回两个方法
- 返回值是个新的promise实例
- 创建成功和失败函数
- 函数中判断是否等于当前返回函数 容易导致回调地狱
- 判断是否继承于父级 说明是个promise 调用then方法 并传入当前的处理函数
- 判断状态并初始化函数 该执行执行 该存储存储 话不多说上代码
then(onFulfilled , onFailed ) {
// 定义成功函数 判断传入类型
onFulfilled =
typeof onFulfilled == "function" ? onFulfilled : (v) => v;
// 失败函数处理 忽略函数之外的其他值 抛出异常
onFailed =
typeof onFailed == "function"
? onFailed
: (reason) => {
throw reason;
return
};
const nowPromise = new MyPromise((resolve, reject) => {
// 定义统一处理函数
const execFun = (fn, val) => {
try {
let res = fn(val);
// 避免回调地狱
if (nowPromise === res) {
reject(new TypeError("Chaining cycle detected for promise #<MyPromise>"));
return;
}
// promise结果 所以直接调用then
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
// 非promise结果
resolve(res);
}
} catch (e) {
reject(e);
}
};
// 成功函数调用
const success = () => {
execFun(onFulfilled,this.value)
}
// 失败函数调用
const failed = () => {
execFun(onFailed,this.value)
}
if (this.status === PENDING) {
this.onSuccessQueue.push(success)
this.onFailedQueue.push(failed)
} else if (this.status === RESOLVE) {
success()
} else {
failed()
}
})
return nowPromise
}
至此为止,我们的Promise也实现完成了,让我们试一试刚才所讲的案例吧
然后,就求一波点赞和关注吧。。。。。。
转载自:https://juejin.cn/post/7040016523108286494