1. Promise简介
1.1 Promise理解
抽象理解:Promise是js中进行异步编程的新解决方案 异步编程:自己的理解:需要按照一定顺序进行的编程 fs,文件操作ajax数据库操作定时器 之前进行异步编程直接通过回调函数的方式进行,会导致回调地狱 回调函数:作为参数的函数回调地狱:回调函数里面嵌套回调函数 代码可读性差不便于异常处理 具体理解: 从语法上说,Promise是一个构造函数从功能上说,Promise对象用来封装一个异步操作并可以获取其成功/失败的结果值为什么使用PromisePromise支持链式调用,可以解决回调地狱问题 回调地狱: 指定回调函数1.2 Promise初体验
Promise是一个对象,包含成功、失败等状态,构造函数,then等方法Promise构造函数的参数是一个包含异步操作的函数该函数有两个参数,resolve和reject,这两个参数同样也是函数形式 resolve在函数内的异步操作成功时调用,会将Promise对象状态设置为成功,并且将成功的结果传递给对象,供then的回调函数使用reject在函数内的异步操作失败时调用,会将Promise对象状态设置为失败,并且将失败的原因传递给对象,供then的回调函数使用 Promise.then()函数具有两个参数,都是函数形式 第一个回调函数在Promise状态为成功时调用 其参数value是Promise的构造函数中回调函数中成功的结果 第二个回调函数在Promise状态为失败时调用 其参数reason是Promise的构造函数中回调函数中失败的原因1.3 Promise实践
1.3.1 fs读取文件
基础回调函数形式:
readFile的第一个参数是文件路径第二个参数是一个回调函数,err表示出错信息,data表示获取到的数据函数主体则根据参数进行操作即可
Promise封装后:
1.3.2 封装fs读取文件
用函数将fs读取操作封装
其返回一个Promise对象
调用函数,使用Promise.then()的方法
原始需要在readFile的参数里面指定回调函数使用Promise后,可以在mineReadFIle后面的then函数中指定回调函数,解决回调地狱问题
1.3.3 util.promisify
作用:自动将异步操作封装成一个Promise
2. Promise属性
2.1 Promise 状态
状态:实例对象中的一个属性 【PromiseState】,三个值
pending:待定,resolved/fullfilled:成功rejected:失败
状态改变
只能由pending改成resolved 或 pending改成rejected状态只能改变一次
2.2 Promise 对象的值
实例对象中的另一个属性 【PromiseResult】保存着异步任务【成功/失败】的结果 只有resolve、reject可以修改其值3. Promise工作流程
调用构造函数前,Promise的状态为pending调用构造函数,执行异步操作 成功则执行resolved(),修改Promise对象状态为resolved,在then函数中回调onResolved,返回一个新的Promise对象失败则执行rejected(),修改Promise对象状态为rejected,在then函数中回调onRejected,返回一个新的Promise对象4. 如何使用Promise
4.1 API
Promise构造函数:Promise(executor){}
executor函数:执行器函数(resolve,reject)=>{ }
resolve函数:内部异步操作成功时我们调用的函数
value => { }
reject函数:内部异步操作失败时我们调用的函数
reason => { }
PS!!!
executor会在Promise内部立即同步调用,意味着代码执行到Promise的对象创建时就会执行其构造函数的回调函数,再执行下面的代码
如这段代码先输出111再输出222
而执行器的异步操作,就在执行器中按照规定的异步顺序执行
Promise.prototype.then()方法:(onResolved,onRejected)=>{ }
参数为两个回调函数:Promise.prototype.then()的prototype表示then是Promise的实例对象的方法,如果没有prototype则表示then是promise函数对象的方法(类似于静态方法)
onResolved,onRejected
onResolved函数:成功的回调函数:(value)=>{ }
onRejected函数:失败的回调函数:
(reason)=>{ }
Promise.prototype.catch()方法:(onRejected)=>{ }
只指定异步操作失败的回调函数,参数只有onRejected onRejected函数:失败的回调函数:
(reason)=>{ }
Promise.resolve方法:(value)=>{ }
作用:接受一个参数,直接返回一个成功或失败的Promise对象,快速将一个值封装成一个Promise对象使用:特点: 如果传入的参数为非Promise对象,则返回的结果为状态为成功的Promise对象如果传入的参数为Promise对象,则返回的结果为状态为传入的Promise对象的状态,返回结果的值为传入Promise的值 这边会报错是因为传入的Promise状态为rejected,这需要Promise有处理错误的回调函数,通过resolve是Promise类的静态方法,不需要实例就能调用
P2.catch((reason)=>{} )
补上回调函数即可相反,如果传入的Promise的状态为resolved,则不需要特意补一个回调函数也不会报错Promise.reject()方法:(reason)=>{}
作用:返回一个值为reason的,状态为rejected的Promise对象 无论传入什么都返回一个状态为失败Promise,即使传入一个成功的Promise对象,返回的Promise的值是传入的成功的Promise对象,但是返回Promise的状态还是失败
Promise.all方法:(promises)=>{}
作用:返回一个新的Promise,当所有Promise都成功,返回的Promise状态为成功,其结果为所有Promise的结果组成的一个数组;当存在失败的Promise,返回的Promise的状态为失败,其结果为失败的Promise的结果
成功情况:
失败情况:
4.2 Promise的几个关键问题
4.3.1 改变Promise对象的状态
三种方式 在构造函数中直接调用resolve
函数在构造函数中直接调用reject
函数在构造函数中直接抛出错误:throw Error
4.3.2 一个Promise指定多个成功/失败的回调函数,都会调用吗
都会执行!
当p的状态变为resolve时,p指定的两个状态变为resolve的回调函数都会执行
4.3.3 改变Promise状态和指定回调函数谁先谁后
问题简化:Promise代码运行时,resolve先执行还是then先执行
注意,指定回调和执行回调函数是不一样的,指定回调类似于p.then()中声明有哪些回调函数,执行回调则是在状态确定之后执行之前指定的回调
两种情况都有可能
先改状态再指定回调: 执行器中是一个同步操作,那么就先做执行器中的操作再执行then语句即指定回调 例如直接在执行器中直接调用 resolve()/reject() 执行器中式一个异步操作,延长更长时间才调用then()同样可以达到效果 先指定回调再改变状态 执行器中式异步操作时,会先执行then,指定回调(但是不执行回调),再回头根据异步操作的顺序进行处理
什么时候能够得到数据?
如果先改变状态,那么当指定回调时,回调函数就会被调用,得到数据如果先指定回调,那么当状态发生改变时,回调函数才被调用得到数据
例子:
promise执行器中是一个异步任务,那么就执行then,指定回调;处理执行器中的异步任务,1000ms后改变状态;再执行成功的回调函数,得到数据
4.3.4 then()方法返回的Promise的对象的状态由什么决定
简单表达:由then()指定的回调函数执行的结果决定
详细表达:
如果回调函数抛出异常,返回Promise状态为【rejected】
如果then()有返回结果,且结果是非Promise对象,例如
则then()方法返回的Promise对象状态为【resolved】,其值为【返回值】,即521
如果then()有返回结果,且结果是Promise对象,例如
则其返回的Promise的状态和值由返回的Promise决定
4.3.5 Promise如何串联多个操作任务
Promise的then()返回一个新的Promise对象,可以形成then方法的链式调用,从而避免回调地狱
let p = new Promise((resolve, reject) => {// 第一次执行异步任务setTimeout(()=>{console.log("first mission")resolve("1000ms later resolve");},1000)})p.then(// 返回一个执行异步任务的Promise,从而第二次执行异步任务,再第一次执行完之后才能执行,链式调用避免回调地狱(value)=>{return new Promise((resolve, reject) => {setTimeout(()=>{console.log("second mission")resolve();},1000)})},).then(value => {return new Promise(resolve => {setTimeout(()=>{console.log("third mission")resolve();},1000)})})
4.3.6 Promise异常穿透?
当使用Promise的then的链式调用时,可以在最后指定失败的回调,而不用每个then都指定一个失败回调
但凡链式调用的过程中由出现失败的情况,最后都会调用最后的失败回调
例如:
then()方法中途返回一个失败的Promise,直接调用最后的catch指定的失败回调
4.3.7 中断Promise链
链式调用过程中,我们希望在某一个then()方法的onResolve回调函数调用过程中,中断Promise的链式调用,只需要返回一个状态为pending的Promise对象
当没有明确返回一个promise对象,then()也会返回一个Promise对象,状态为【resolved】,结果为【underfined】
因为then方法的回调函数调用依据是Promise的状态是resolved或者rejected,没有pending对应的回调函数,所以后面的都没法执行了
例如:
自定义封装看到P31
5. Promise自定义封装
6. async和await
6.1 async
async是一个函数修饰符
函数的返回值是Promise对象
Promise对象的结果由async函数执行的返回值决定
和then一样:分成三种情况执行返回值是非Promise对象,是一个Promise对象,是抛出异常
6.2 await
获取Promise的成功的结果
await右侧表达式一般为Promise对象,但也可以是一个其他值如果表达式时Promise对象,await返回的Promise成功的值如果表达式是其他值,直接将此值作为await的返回值
6.3 async 和 await
理解:async是函数的修饰符,表示这个函数内部可能会存在异步操作;await是一个表达式修饰符,表达式一般跟Promise(内含异步操作),返回的值是Promise成功的值,并且await下面的代码必须要等await后的异步操作完成后才可继续执行,保证了时序
6.3.1 二者联系与区别
await必须要卸载async函数中,async中可以没有await如果await的Promise失败了,会抛出一个异常,需要try catch进行捕获处理6.3.2 应用例子 — 发送axios请求
6.3.3 应用例子 — 读文件 + 闭包概念
需求:读取多个文件的内容,并将其拼接
使用回调函数的方式进行
所以console.log输出的时候可以访问到 data1和data2
async + await
首先导入util包,利用util.promisify()
方法,将readfile方法转化成一个promise对象并返回 利用readfile的Promise对象,使用await修饰,返回文件内的内容给data,之后一次性拼接优点 避免重复回调,可读性高出错可以直接用一个大的try catch包裹,而不用每次回调都抛出错误
Extra:闭包
所以console.log输出的时候可以访问到 data1和data2
async + await
[外链图片转存中…(img-n9QLdX91-1662185609736)]
首先导入util包,利用util.promisify()
方法,将readfile方法转化成一个promise对象并返回
[外链图片转存中…(img-WGeX8p3x-1662185609737)]
利用readfile的Promise对象,使用await修饰,返回文件内的内容给data,之后一次性拼接优点 避免重复回调,可读性高出错可以直接用一个大的try catch包裹,而不用每次回调都抛出错误
Extra:闭包
6.3.3 利用到了函数闭包:在上述代码中父对象的变量对于所有子对象都是可见的,反之则不成立;而在我的理解中,闭包是能够读取其他函数内部变量的函数。又因为JavaScript中只有函数内部的子函数才能够获取函数的局部变量,所以在JavaScript中,可以简单理解闭包为:在函数内部的函数