三. 构思异步存储数据的思路
我们现在要明确一点,我们上面的代码 resolve
到底被调用了吗?会不会压根就是 resolve
没被调用才导致现在 then
拿不到数据呢?
我们在 resolve
里加一句打印,我们看看到底是不是这个原因。
在控制台可以清楚的看到,虽然没有第一时间执行,但是我们的 resolve
是确确实实执行了的。
清楚了这一点,我们需要理清楚思路。既然你 resolve
是在一秒后才会执行。如果我是 then
函数我可能会这样想:“resolve
函数啊,如果你执行完毕了以后你再通知我该有多好啊,别让我一个人先走一步~”
顺着这个思路,我们就要在 resolve
这里构思有什么方法可以去通知 then
。
这里我们再想想,我们的数据是在哪读取的?
没错,是在 then
函数的第一个回调函数 onFulfilled
里去读取的。那么有没有一种可能,我让你 resolve
去帮我执行这个 onFulfilled
函数不就更省心了吗?
这样我 then
函数坐享其成不是更美吗?
那么问题来了, then
函数的回调函数其实只能在 then
的作用域去调用,什么意思呢?我们给函数定义参数的时候,实际上是执行了下面的形参被实参重新赋值的操作。所以我们的参数对外是压根看不见的。
换而言之,resolve
函数压根就不知道有 onFulfilled
这个函数!!!。
这就麻烦了,这怎么办呢?别急我们再定义一个变量,叫做 callBackFn
,这个变量也是一个函数。
它用来干嘛呢?我们稍后揭秘,我想现在你的代码应该是下面这样子。
四. 神奇的回调函数
我们由上面可知,我们主要是因为 then
方法在 state==='pending'
的时候,没办法做任何操作才无法拿到异步函数传递过来的数据的。
注意接下来是全文重点: 当我在 state==='pending'
的时候,我把刚刚定义好的 callBackFn
函数值设置为 then
中 onFulfilled
回调函数的值。
接下来就是最神奇的一步操作,我再把 callBackFn
放到 resolve
函数拿到数据之后执行!
别着急,我们先试一下行不行再一步一步解释原因。还是之前的代码,按照下面代码的逻辑,我们应该会在两秒以后看到控制台输出 看看行不行
这个字符串。
我们看一下效果:
什么情况,还真可以!!
不要觉得这是什么黑魔法,其实思路特别特别简单。顺着之前的思路,当我们的代码执行以后。首先会执行。
我们的 resolve
就被丢进了 宏任务队列里去了。
4.然后主线程继续往下走,就走到了 then
函数中。
接下来我们就有需要跳到 MyPromise
类中去看 then
函数调用后发生的情况。首先我们非常明确的是,这时候由于 resolve
还没执行,所以我们的 state
还是 pending
状态。
那么这时候就会走 then
函数的第一个判断逻辑。
它会将我们 then
函数的第一个参数 onFulfilled
赋值给我们之前定义好的 callBackFn
变量。
至此,任何其他事情还没发生,然后我们静等了两秒以后,resolve
函数从任务队列里被推进了主线程。我们需要转头去看 resolve
函数执行。
我们可以非常非常清晰的看到,谁在最后执行了?没错!!就是我们两秒之前 被赋值为 onFulfilled
函数的 callBackFn
函数!!!
千万不要迷,这里完完全全就等价于两秒后 resolve
函数帮我去调了 then
函数的 onfulfilled
函数。
只不过我们没有办法直接去调用定义在 then
函数作用域的那个 onFulfilled
,而是通过了一个中间变量的形式,“间接去调用了它”。这便是 JS 回调函数的魅力所在。
五. 修复 bug
但是现在我们又产生了一个新的问题,当我们的 resolve
又变成了同步赋值的时候,我们看看是什么后果。
看一下控制台输出什么?
什么情况?我们的 callBacnFn
又不是一个 function
了?
你需要清晰的知道,如果我们的 resolve
没有放进 setTimeout
里的执行的话,它就是一个同步代码,同步代码的话,它就会在 then
函数执行之前执行。
反应在 MyPromise
类里面的执行过程就是。我们的 callBacnFn
在被赋值之前就被调用了,那肯定会报错啊,因为我们既没有给它赋初始值,又没有被 then
函数调用,所以它现在就是 undefined
。
那怎么办呢?其实非常非常简单,我们只需要在执行 callBackFn
之前,判断一下它存在不存在就可以了。
由于是教学,我们写一种更加容易理解的代码,如果有我就执行,如果没有我就不执行,就是这么简单。
我们看一下控制台输出还有问题吗:
ok,没问题了~
结语:
至此距离我们完成自己的 MyPromise
类已经成功了一大半!我相信通过消化这一篇的内容,你会收获很多很多额外的知识。是不是有一种原来 Promise
不过如此的感觉~
其实有很多很多东西都是用很基本的函数,通过很巧妙的设计去完成一些看起来很复杂的逻辑。在下一章我们会迎来最后的几个关键点,如:微任务的创建,then函数的链式调用,希望你能坚持下去。🎁
如果你暂时还没读懂,没关系,我建议你先去看一下我们上面的几个支线任务 再回过头细细品味本篇内容。距离我们手撕 Promise
已经近在咫尺了,你的登神长阶完成了几章呢?加油,一起进步呀!冲鸭~