【momo的源码基石】Promise

145 阅读6分钟

前言:呜呜呜,春招开始了,但是俺的代码内功还不够。在博主的推荐下,我定下了看源码的目标,并从axios开始努力!在阅读的过程中,我发现了Promise的痕迹,巧的是当天遇到一个同学对promise的使用并不了解,因此写下此文。

Promise解决的问题——回调地狱

提出问题

  同学的需求是这样的:在依赖a请求返回的数据后,进行b请求的发起,最后返回b请求的内容,且在代码中不希望使用过多的async和await
  对此,他的解决方案如下图所示:

c78604c602216492bc2d73b3bd197b1.jpg

注意点

  细心的同学会发现,其实他这样能对上一个请求的内容进行依赖,但是最后无法返回目标请求的数据result。其中有几个点是需要注意的:

  1. then的数据不能被直接return返回
  2. 就算被直接返回,在原来的函数(第33行代码所在的函数)中并没有显示返回值,如果直接获取,那就是undefined
  3. 如果直接返回第33行代码代表的promise,并不能获取的数据,其状态是pending,包装的值依然是undefined。

解决方案

大家给他的方案是:promise套着axios,then到最后那个需要的请求,再resolve

  封装函数进行请求,在外部获取resolve的参数就能得到目标值result,再进行返回。
微信图片_20230210140042.png

Promise简介

  Promise是ES6新增的引用类型,而async/await则是ES8规范新增的关键字。Promise可以通过new进行构造,需要传入参数(函数)对其进行构造,否则将会Uncaught TypeError TypeError的报错。
  Promise具有3种状态,分别是

  • pending(待解决)
  • fulfilled(兑现)
  • rejected(拒绝)

  Promise的状态是私有的,不能直接被读取到,不能被外部代码进行修改,通过Promise提供的执行函数,可以对其状态进行改变。

我对Promise的理解:Promise就是通过状态的改变,对异步任务执行完成后的状态进行获取。

Snipaste_2023-02-10_14-48-18.png

你可能不知道的Promise

执行函数都是同步执行的

  Promise的状态处理都是同步的执行的,一开始的时候由执行函数进行初始状态的设定,后面可以根据resolve和reject进行状态的改变。也许,你会在状态执行函数中使用异步任务,这种情况可以理解成,同步的添加异步任务。

Snipaste_2023-02-10_15-30-22.png

Promise具有异步性

  在Promise的reject状态执行函数中,会抛出一个异常,但是我们使用同步的方式try/catch进行获取是不能成功的,可以理解为,该异常是在异步中抛出

Snipaste_2023-02-10_15-45-00.png

Promise中resolve和reject的异同

相同点:

  1. Promise中的resolve和reject都能让Promise的状态进行改变;
  2. 二者都是互斥的,当已经执行其中一个,另外一个函数都会被静默处理(即状态不会改变,但是下面的代码依然能执行);
  3. Promise.resolve()和Promise.reject()都会将放入值进行包装,且只能包装一个对象,传入多个时另外几个都会被静默处理。


不同点:

  1. 代表的状态不同,resolve代表fulfilled(兑现),而reject代表rejected(拒绝);
  2. 当变成reject状态时,会爆出异常,但是用resolve改变时不会;
  3. Rromise.resolve()包裹参数具有幂等性,当Promise.resolve()包装被新的Promise.resolve()包裹的时候,最后的地址值等于他本身;而Promise.reject()被其本身包裹时,地址值发生改变。
image.png

Async/Await

  async和await是ES8增加的关键字,大伙在平时处理的时候都会经常使用,但是对于其中状态的改变等却很少被注意到,下面是我对其的一些理解。

Async

  Async是加在函数前面的关键字,加了async关键字后,函数里面的代码会正常实行,但是当其正常返回值时,返回的值会被Promise.resolve()进行包裹;若是内部代码出现抛出异常的操作,抛出的异常则被Resolve.reject()包裹;如果是被阻塞暂时跳出函数呢?(你先别急,急也没用,请看下文)
Snipaste_2023-02-10_21-17-09.png

Await

  Await只能在加了Async的函数中使用,并加载在其中的某一行代码或几行代码中。await处理的代码是异步的,await所在的行下面的代码会在await处理的异步函数行执行后才能执行。
  Await其实是期待promise的,但是不是Promise的也能被修饰。

Async和Await一些火花

  在上文中,我们对Async的正常reurn、throw作出归纳,对暂时跳出函数挖了坑,现在就给将其填补。各位看官可以看看下面代码段的执行。

image.png

  有趣的事情了,不管是node环境还是浏览器环境,都出现一个Pending状态的Promise

image.pngimage.png

  接下来我们对其运行流程进行分析:

  1. 运行同步任务add(1,2)
  2. 进行num1+num2的操作
  3. 运行await 将result放在微任务队列中,并暂时放弃函数后面代码的执行
  4. 跳出add函数,此时,第107行的a会得到一个Promise对象的地址,他的状态未被改变,正是出现的Pending
  5. 执行第108行进行打印
  6. 执行第109行,而setTimeOut是宏任务,放在执行栈、微任务队列后执行
  7. 返回第103行代码,检查相关的任务
  8. 执行第104行代码,返回result+1的结果,此时Promise的状态被改变了
  9. 最后打印a的值,正是状态fulfilled的Promise

  也许屏幕前的你可能学废了,那接下来可以趁热打铁来康康下面的几个变式,分析一下终端的打印。
变式1:
image.png
变式2:

image.png
image.png

Promise在源码中的应用

  好了,言归正传,本文的标题是【momo的源码之路】就来点和源码有关的吧!
  Axios相信大家都用过,用来发送请求和后端进行连接,可能大家选择使用Axios带来的原因大多数是因为其流行程度。但是Axios被广泛选择并仅仅如此,Axios的卖点并不只是常用的网络请求、拦截器,Axios中对配置的合并友好、对请求的拒接发送、对请求批量发送都是他受欢迎的点,可能我们只是少用。
  根据源码,Axios是非常依赖Promise的,Axios的批量发送,就是依靠Promise.all();拒接发送则是通过从Promise内部改变Promise的状态进行实现的。Cancel请求的源码暂时挖个坑,也可以看看其他博主对Axios源码的解读。

image.png

  综上所述:基础不牢,地动山摇!希望你能和我一起读源码、固基础。智者不入爱河,寡王一路offer。
  我是momo,我们下期见!


微信图片_20230210133124.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情