手写 Promise(中篇)
前言: 阅读前需要拥有我们前面的五个支线任务的通关钥匙🔑 (0/5),
请完成你的登神长阶
本文紧接着前篇的主线任务,建议没看《前篇》的小伙伴先去看一下《前篇》再回过头看本篇内容。(没浏览过前面文章的小伙伴,建议在父母的陪同下完成阅读。📖)
一. 思考 MyPromise 现有的问题
如果你跟进了之前的文章,那么下面应该是你目前的代码
虽然现在好像我们的逻辑都在顺着一步一步走,但是这里面其实有一个非常严重的 bug 。我们暂时先不揭秘,反过来我们先思考一下🤔,在原生的 Promise
如果我们在构造器函数内多次调用 resolve
函数的话,它保存的值是会以哪次为准呢?
这里我们直接揭晓答案。结果是 Promise实例 只会保存第一次调用 resolve
函数保存的那个数据。
但是现在反过头来看看我们目前的逻辑。
在控制台输出的结果是:
造成这种情况的原因非常简单,因为按照上面的写法,我们相当于在 executor
里连续调用了三次 resolve
如下所示:
结果显而易见,它调用了三次,按照代码逻辑,它 result
前两次的值被覆盖了,它保存了最后一次调用 resolve
时存入的值。
这里的解决方法依旧非常简单,我们不需要借助其它东西,我想你也能大概猜出来。这里还是需要借助 #state
来判断我们是否继续执行过了 resolve
。
别忘了,我们在 constructor
刚刚执行的时候最先修改的就是 #state
的值,所以我们只需要在 resolve
和 reject
函数执行之前,先判断当前 #state
是否不是 pending
,如果不为 pendng
则说明之前已经执行过了,则直接返回,不进行任何其他操作。 reject
同理。我们再看一下控制台现在的的样子。
嗯,现在确实不会再执行后面的 resolve
函数了。
二. 异步数据的存储
写到这里你可能会发现,我们现在的 MyPromise
其实是一个假的,是一个只能保存同步数据的普通类而已。
因为我们现在的 Promise
是不能读取到异步存储到数据的。 这也是 Promise
的核心功能 保存异步数据 。因为我们向后端请求数据绝对不是一瞬间数据就过来的,而是会有时间的延迟,过一段时间才需要调用 resolve
去保存。
什么意思呢?我们先看原生的 Promise
如果使用异步代码执行 resolve 的话是什么情况。 我们在 executor
函数体内开启一个定时器,在一秒以后去执行 resolve
保存数据。
我们在控制台看一下输出结果:
可以清楚的看到,我们在 then
方法过了一秒,成功读取到了 result
中的数据。
现在我们回过头看看我们的 MyPromise
是什么效果。
观察上面的代码,我们推算出在理想状态,控制台会输入一个数字 1。然而结果确实---控制台空空如也。
这是怎么回事呢?我们一步一步分析。
当我们的代码执行时,首先会去执行这一段代码。
根据前面的知识可以得出,我们的 executor
函数会马上开始执行。
紧接着就遇到 setTimeout
函数。经过前面的学习,我们知道 setTimeout
的回调函数会被放进宏任务队列,结果就是我们的 resolve
被放进了宏任务队列去乖乖排队去了,
根据从上往下的执行顺序,马上就会去执行 then
方法。
注意: 这时候我们需要去看 MyPromise
类里的执行情况,才能知道原因
当我们的 executor
执行后,我们的 this.resolve
会在一秒后才会执行,所以 state
的状态还是 pending
。 而此时我们的 then
又是在主线程执行的代码。所以自然而然,then
函数不会有任何结果。
本着严谨的态度,我们进一步验证一下我们的推断是不是正确的。让我们在 then
方法执行的时候打印一下 this.state
。
看一下控制台:
果然是这个原因,那现在怎么办呢?🤔