共计 3678 个字符,预计需要花费 10 分钟才能阅读完成。
前言: 浏览前须要领有咱们后面的五个 干线工作 的通关钥匙🔑 (0/5),
- JS 代码运行机制
- 加深了解回调函数
- 手写“回调天堂”
- 宏工作和微工作
- 手写 Promise(前篇)
请实现你的登神长阶
本文紧接着前篇的主线工作,倡议没看《前篇》的小伙伴先去看一下《前篇》再回过头看本篇内容。(没浏览过后面文章的小伙伴,倡议在父母的陪同下实现浏览。📖)
一. 思考 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
。看一下控制台:
果然是这个起因,那当初怎么办呢?🤔
三. 构思异步存储数据的思路
- 咱们当初要明确一点,咱们下面的代码
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
曾经近在眉睫了,你的 登神长阶 实现了几章呢?加油,一起提高呀!冲鸭~