前言:呜呜呜,春招开始了,但是俺的代码内功还不够。在博主的推荐下,我定下了看源码的目标,并从axios开始努力!在阅读的过程中,我发现了Promise的痕迹,巧的是当天遇到一个同学对promise的使用并不了解,因此写下此文。
Promise解决的问题——回调地狱
提出问题
同学的需求是这样的:在依赖a请求返回的数据后,进行b请求的发起,最后返回b请求的内容,且在代码中不希望使用过多的async和await
。
对此,他的解决方案如下图所示:
注意点
细心的同学会发现,其实他这样能对上一个请求的内容进行依赖,但是最后无法返回目标请求的数据result。其中有几个点是需要注意的:
- then的数据不能被直接return返回
- 就算被直接返回,在原来的函数(第33行代码所在的函数)中并没有显示返回值,如果直接获取,那就是undefined
- 如果直接返回第33行代码代表的promise,并不能获取的数据,其状态是pending,包装的值依然是undefined。
解决方案
大家给他的方案是:promise套着axios,then到最后那个需要的请求,再resolve
封装函数进行请求,在外部获取resolve的参数就能得到目标值result,再进行返回。
Promise简介
Promise是ES6新增的引用类型,而async/await则是ES8规范新增的关键字。Promise可以通过new进行构造,需要传入参数(函数)对其进行构造,否则将会Uncaught TypeError TypeError
的报错。
Promise具有3种状态,分别是
- pending(待解决)
- fulfilled(兑现)
- rejected(拒绝)
Promise的状态是私有的,不能直接被读取到,不能被外部代码进行修改,通过Promise提供的执行函数,可以对其状态进行改变。
我对Promise的理解:Promise就是通过状态的改变,对异步任务执行完成后的状态进行获取。
你可能不知道的Promise
执行函数都是同步执行的
Promise的状态处理都是同步的执行的,一开始的时候由执行函数进行初始状态的设定,后面可以根据resolve和reject进行状态的改变。也许,你会在状态执行函数中使用异步任务,这种情况可以理解成,同步的添加异步任务。
Promise具有异步性
在Promise的reject状态执行函数中,会抛出一个异常,但是我们使用同步的方式try/catch进行获取是不能成功的,可以理解为,该异常是在异步中抛出
。
Promise中resolve和reject的异同
相同点:
- Promise中的resolve和reject都能让Promise的状态进行改变;
- 二者都是互斥的,当已经执行其中一个,另外一个函数都会被静默处理(即状态不会改变,但是下面的代码依然能执行);
- Promise.resolve()和Promise.reject()都会将放入值进行包装,且只能包装一个对象,传入多个时另外几个都会被静默处理。
不同点:
- 代表的状态不同,resolve代表fulfilled(兑现),而reject代表rejected(拒绝);
- 当变成reject状态时,会爆出异常,但是用resolve改变时不会;
- Rromise.resolve()包裹参数具有幂等性,当Promise.resolve()包装被新的Promise.resolve()包裹的时候,最后的地址值等于他本身;而Promise.reject()被其本身包裹时,地址值发生改变。
Async/Await
async和await是ES8增加的关键字,大伙在平时处理的时候都会经常使用,但是对于其中状态的改变等却很少被注意到,下面是我对其的一些理解。
Async
Async是加在函数前面的关键字,加了async关键字后,函数里面的代码会正常实行,但是当其正常返回值
时,返回的值会被Promise.resolve()
进行包裹;若是内部代码出现抛出异常
的操作,抛出的异常则被Resolve.reject()
包裹;如果是被阻塞暂时跳出函数
呢?(你先别急,急也没用,请看下文)
Await
Await只能在加了Async的函数中使用,并加载在其中的某一行代码或几行代码中。await处理的代码是异步的,await所在的行下面的代码会在await处理的异步函数行执行后才能执行。
Await其实是期待promise的,但是不是Promise的也能被修饰。
Async和Await一些火花
在上文中,我们对Async的正常reurn、throw作出归纳,对暂时跳出函数挖了坑,现在就给将其填补。各位看官可以看看下面代码段的执行。
有趣的事情了,不管是node环境还是浏览器环境,都出现一个Pending状态的Promise。
接下来我们对其运行流程进行分析:
- 运行同步任务add(1,2)
- 进行num1+num2的操作
- 运行await 将result放在微任务队列中,并暂时放弃函数后面代码的执行
- 跳出add函数,此时,第107行的a会得到一个Promise对象的地址,他的状态未被改变,正是出现的Pending
- 执行第108行进行打印
- 执行第109行,而setTimeOut是宏任务,放在执行栈、微任务队列后执行
- 返回第103行代码,检查相关的任务
- 执行第104行代码,返回result+1的结果,此时Promise的状态被改变了
- 最后打印a的值,正是状态fulfilled的Promise
也许屏幕前的你可能学废了,那接下来可以趁热打铁来康康下面的几个变式,分析一下终端的打印。
变式1:
变式2:
Promise在源码中的应用
好了,言归正传,本文的标题是【momo的源码之路】就来点和源码有关的吧!
Axios相信大家都用过,用来发送请求和后端进行连接,可能大家选择使用Axios带来的原因大多数是因为其流行程度。但是Axios被广泛选择并仅仅如此,Axios的卖点并不只是常用的网络请求、拦截器,Axios中对配置的合并友好、对请求的拒接发送、对请求批量发送都是他受欢迎的点,可能我们只是少用。
根据源码,Axios是非常依赖Promise的,Axios的批量发送,就是依靠Promise.all();拒接发送则是通过从Promise内部改变Promise的状态进行实现的。Cancel请求的源码暂时挖个坑,也可以看看其他博主对Axios源码的解读。
综上所述:基础不牢,地动山摇!希望你能和我一起读源码、固基础。智者不入爱河,寡王一路offer。
我是momo,我们下期见!
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情