likes
comments
collection
share

javaScript 进阶之路 --- 《手写 Promise(前篇)》

作者站长头像
站长
· 阅读数 35

手写 Promise

前言: 该来的还是来了,经过完成我们前面的四个支线任务

终于到了我们的主线任务 ---《手写 Promise》。对于前端来讲,当你完全理解 Promise 的设计思路,那么你会对你之前所了解的 JS 世界,有一个全新的认知。

不知各位是否有过下面这样的想法:

--“我感觉我现在知识储备差不多了,接下来如何进阶呢?”

其实这个问题谁都无法告诉你准确答案。只有当你在某一天掌握了你之前不知道的知识点,并且完全领悟它的时候。你脑子里就会突然有种 “知识升华” 的感觉。(我也无法准确描述那种感觉,就好像突然悟了一样)脑子里会瞬间把之前所有知识串联起来,各个知识点不再是一个一个的碎片🧩。

这种感觉就是在我某一天读懂 JS 回调函数真正想表达出的思想,并且手写出 Promise 涌现的。当你某天有过一次这样感觉后,你就不会再去问别人关于如何进阶这种问题了,因为那时候的你,其实已经完成了当前知识的进阶,当你回过头看之前的代码时,你会发现不一样的世界,是那种拨云见日的感觉。

⚠️注意:本文的内容需要你对回调函数和宏任务微任务有比较清晰的认识,请不太懂的小伙伴在家长的陪同下认真观看


一. 文件准备

本文需要你准备的文件非常简单,随便在你的目录文件夹下创建一个 myPromise.ts 文件,对 .ts 不太熟悉的小伙伴不需要担心,本篇用到 ts 的相关内容很少。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

二. 实现 MyPromise类 的构造器函数

  1. 首先我们定义一个叫做 MyPromise 的类。在接下来我会顺着原生的 Promise 一步一步帮你去理解 Promise 的实现思想。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  2. 在上一章内容我们了解了,Promise 类会接收一个叫做 executor 的函数来初始化我们的 Promise 类的实例。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 ok,并且我们还特别强调了,executor 就是一个普普通通的函数而已。那么我们就可以像下面这样,在 MyPromiseconstructor 函数内定义一个形参,来准备接收在初始化时传递给我们的实参数。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 我相信聪明的你一定可以看出来,我们其实在 new 一个 Promise 实例的时候,传递给 Promise 类的函数就是我们刚刚定义的这个 executor 函数的实参。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  3. OK,我们继续。我们还知道 executor 函数会被传递两个参数。这两个参数分别是一个叫做 resolvereject 的函数!注意,它们两个是函数!!所以我们就可以像下面这样写。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 好像飘红了?是的,因为在 MyPromise 结构体内,还没有这两个函数。那怎么办呢?🤔,这还不简单?没有我们就自己造呗~

  4. 可以看到我们自己定义了两个函数,好像还是飘红... javaScript 进阶之路 --- 《手写 Promise(前篇)》 这里就需要用到 this 关键词,来告诉 executor 我要传递的是类本身的方法。所以正确的方法应该下面这样。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 至此,我们的 executor 函数就已经写好了。

三. 实现 resolve,reject 函数

  1. 这里我们先实现 resolve ,别着急。我们先看原生 Promise 是怎么使用的。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 我们可以得知,resolve 函数可以被传递一个参数,所以我们可以更进一步得出。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 我们先来测试一下我们的思路是不是对的。我们先 new 一个实例保存一个数据看看。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 javaScript 进阶之路 --- 《手写 Promise(前篇)》 嗯...看来有点那味道了。🍦

  2. 同理 reject 函数也是这种写法。到这一步,我想你的 MyPromise 类应该长这个样子。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

四.实现 then 方法

  1. 我们先回忆一下,原生的 Promise 读取数据的时候,是在实例的 then 方法上读取的。这里我们就需要提供一个变量去接收 resolve 传递的值。并且需要在 MyPromise 类中提供一个 then 方法,去读取传递过来的数据。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  2. 这里先停一下,我们想一想。我们的 result 是不希望被实例引用的。什么意思呢?如果我们按照上面的写法,是会引发这样的错误的。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 实例竟然可以直接去引用这个 result ,这是我们不希望看到的。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 这里需要用到的知识是:假如我们不希望实例调用某个属性,方法也很简单,只需要在前面加上一个 #号即可。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  3. 接下来是本文的第一个重点。then 函数该怎么设计?我们先看原生Promise 实例的身上 .then 方法是怎么使用的。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 由之前的知识我们可以知道,then 方法也是接受两个回调函数作为参数的。并且第一个回调函数的参数会被传递 resolve 保存的值,第二个回调函数的参数会被传递 reject 保存的值。

  4. ok,那我们先不考虑那么多,直接先给 then 函数传两个参数,这两个参数也是两个函数。第一个参数我们就起一个叫 onFulfilled的函数 ,它对应着 resolve 保存的值。第二个参数我们就叫 onRejected 吧,它也应该是一个函数。于是我们就可以补充 then 函数的内容,它会被传递我们通过 reject 保存的那个结果。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 下面的代码应该是你目前写出来的样子。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

五. MyPromise 的三种状态

  1. 等等,我们好像忘了一个很关键的东西,Promise 的三种状态!!!还记得吗?Promise 在初始化的时候是有三个状态的,分别是 pendingfulfilledrejected。这三种状态分别影响着 then 函数中我们取到的值。

  2. 知道这一点,我们马上就应该想到,我需要再定义一个变量,来存放这三个状态值。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 并且还有一点,pending 状态是会在初始化,也就是还没调用 resolve 或者 reject 函数之前的状态。 所以我们就需要在 constructor 构造函数调用 executor 之前将 state 的状态信息改为 pending,对应就是下面这行代码的意义。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  3. 并且我们可以推算出,在 resolve 函数内,和 reject 函数内需要分别修改状态值为 fulfilledrejected,就如同下面的写法一样。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  4. 由于我们 then 函数会根据状态去做相应传递的参数不同,所以理所当然的需要修改为下面的写法。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

  5. 下面应该是你目前的代码。 javaScript 进阶之路 --- 《手写 Promise(前篇)》

六. 创造一个 MyPromise 实例

  1. 目前看起来好像我们的逻辑非常严谨对吧,我们别着急往下写,我们自己去调用一下看看是什么样子。我们先自己 new MyPromise 生成一个实例看看。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 按照我们的推算,控制台应该会出入一个字符串韩振方。 我们看一下结果: javaScript 进阶之路 --- 《手写 Promise(前篇)》 什么情况?竟然报错了?别着急,我们分析一下错误。
Uncaught TypeError: Cannot set properties of undefined (setting '#result')

这里给出的错误信息是,我们不能给 undefined 设置 #result 的属性。 怎么回事啊?我们怎么在给 undefined 设置属性?我们不是在给 MyPromise 实例设置属性吗?我还就不信了,我非得打印一下看看,我们在 resolve 里面打印一下 this看看到底这个 this 是谁 。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 看一下控制台: javaScript 进阶之路 --- 《手写 Promise(前篇)》 emm...好像还真是 undefined

  1. 到底怎么回事呢?🤔 其关键点在于下面这段代码。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 在这里我们看似在引用实例自身去调用 resolve,实际上在这里调用者的其实是 window 对象。不过因为我们在 Class 类里面是默认开启严格模式的,如果丢失了 this ,并不会将 this 默认指向全局对象 windowjavaScript 进阶之路 --- 《手写 Promise(前篇)》

  2. 所以我们需要干一件非常非常重要的事情,没错,就是绑定 thisjavaScript 进阶之路 --- 《手写 Promise(前篇)》 因为我们 constructor 函数里的 this 总是会指向实例本身,所以我们需要在 调用resolve 函数和 reject 函数之前,需要在 excutor 的参数提前绑定好 this 才可以。所以我们现在的代码应该是这个样子: javaScript 进阶之路 --- 《手写 Promise(前篇)》

  3. 让我们再次调用一下我们自己的 MyPromise ,来看一下现在能不能成功读取我们保存的数据。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 javaScript 进阶之路 --- 《手写 Promise(前篇)》 可以看到,我们已经成功读取了我们通过then方法读取到了 resolve 传递过来的数据。

  4. 到这里你可能好奇,为什么 then 方法不用绑定 this。因为你别忘了我们的 then 是怎么去调用的。 javaScript 进阶之路 --- 《手写 Promise(前篇)》 可以看到,我们的 then 是通过对象一个属性名去调用的,那么它的 this 百分百就是 data 实例对象。它是不需要我们考虑 this 指向问题的。

总结

在本文主要引导大家构造好一个 Promise 的骨架,大家可以先消化一下,在下一篇内容我会更深入的讲解其中的细节。🎁

转载自:https://juejin.cn/post/7176639726923841591
评论
请登录