什么!?》一文打通Promise和async/await!!!

466 阅读8分钟

Promise和aysnc/await总结

引言

为什么要了解Promise?

Promise正如其名【承诺】就是一个异步任务 它的核心是进行链式调用,通过【链式调用】可以避免回调地狱【回调嵌套回调无穷尽也】的问题

同时,配合官方加成的语法async和await,就可以实现回调的避免,从而让代码更加优雅而好理解,让我们在实际开发中能够写好异步函数。

首先 认识Promise

Promise有两个阶段、三个状态

两个阶段:未决阶段和已决阶段

三个状态:

  • 未决阶段:挂起状态
  • 已决阶段:完成状态、失败状态

image.png

Promise最开始为未决阶段unsettled,状态为pending(挂起状态),我们需要将Promise从未决阶段转换成已决阶段

Promise(callback)
new Promise((resolve,reject)=>{  });

这里需要讲到Promise的参数了 Promise(callback) 对,Promise的参数为一个回调函数,其中按照先后来看,有按两个参数

resolvereject 分别能够将Promise从挂起状态pending转换成 完成fulfilled/失败rejected状态

在一个Promise中

如果运行resolve()则转换成完成状态

如果运行reject()则转换成失败状态

image.png

我们需要根据实际逻辑决定是调用resolve()还是reject()

在转为已决阶段之后,Promise就会返回一个值,这个值是传在resolvereject中的数据。

接下来需要进行后续处理

Promise的后续处理

Promise的后续处理对应着一个函数.then()

其实不是.then()方法,应该说:Promise对象可以通过.访问符来调用then()方法。

.then()方法中可以按顺序放入两个回调函数,

  • 第一个回调函数是对成功的后续处理
  • 第二个回调函数是对失败的后续处理

这两个回调函数都可以写上,实际调用哪个要看Promise对象已决阶段是成功还是失败。

而现在,一般使用.then()中放入一个回调函数,具体规范为:

  • .then专门进行成功的后续处理
  • .catch专门进行失败的后续处理

Promise的链式调用

像这样

Promise((resolve,reject)=>{
    // ...
    
}).then((data)=>{
    // 成功后续处理逻辑

}).catch((reason)=>{
    // 失败后续处理逻辑

});

这上面就用到了Promise的核心:【链式调用】

链式调用避免了回调地狱,也就是说不需要【回调函数嵌套回调函数无穷尽】了

【Promise后续处理】概念纠正:

要知道:后续处理是一个Promise,后续处理只是后续处理 而.then()中回调函数进行后续处理完毕后返回的是一个Promise

这个Promise的状态仅被处理逻辑中 return 决定

  • 如果处理逻辑报错或者手动报错,则状态为Rejected,携带值为错误信息,用于之后的catch的回调函数中使用
  • 只要不出错,状态就是Fulfilled,携带值为【return中的值】,用于之后的then中的回调函数中使用。

如果说then中的回调函数尚未执行,而把其赋值给一个变量,可以发现这个变量的Promise对象对应的状态为pending。 【then()中的回调函数尚未执行并不妨碍then()返回一个Promise】。只是这个Promise的状态和值在then()中的回调函数执行完毕后再被确定。

如果.then()/.catch()链接的Promise对象的状态为相反,则只作为传递者,或说会被跳过,直到有处理相应状态的函数为止。

由于Promise是异步任务,所以同步代码不会等待Promise执行,以下为一个典型示例:

// 下面代码的输出结果是什么 
const pro1 = new Promise((resolve,reject)=>{   
let data = 1;   
console.log(data);   
setTimeout(()=>{     
    resolve(data);   
},1000); }) 

const pro2 = pro1.then((data)=>{   
    console.log(++data);     
    return data; 
}) 

const pro3 = pro2.then((data)=>{   
    console.log(++data);   
    return data; 
}) 

console.log(pro1,pro2,pro3); 
// 
setTimeout(()=>{   
    console.log(pro1,pro2,pro3); 
},2000); 
/**  
* 完整执行顺序总结 
    同步代码:   
        定义 pro1,输出 1。   
        定义 pro2 和 pro3,输出 pro1, pro2, pro3 的状态(Pending)。 
    异步代码:   
        1秒后 pro1 完成,输出 2。           
                pro2 完成,输出 3。           
                pro3 完成。   
        2秒后,输出 pro1, pro2, pro3 的最终状态和值。  
        1 Promise { <pending> } Promise { <pending> } Promise { <pending> } 
        2 
        3 
        Promise { 1 } Promise { 2 } Promise { 3 } 
*/

Promise的静态方法

工具方法有 Promise.resolve() Promise.reject() 分别直接返回一个成功状态/失败状态的Promise对象

说明:

Promise.resolve(1);
// 等价于
new Promise((resolve)=>{
    resolve(1);
})

而其他静态方法

Promise.all([Promise对象数组])

有三种情况

  • Promise对象运行结果都为完成状态,则返回一个成功状态的Promise对象,数据为装有所有Promise运行resolve(data)中data的数组.
  • 存在Promise的运行结果为失败状态,则返回一个失败状态的Promise对象,数据为首个失败的Promise对象的失败原因。
  • 存在Promise的运行状态仍为挂起状态(Pending),则返回一个Pending状态的Promise对象

Promise.any([Promise对象数组])

有三种情况

  • 存在Promise对象的运行结果为完成状态,则返回一个完成状态的Promise对象,数据为首个完成的Promise对象的数据
  • Promise对象的运行结果都为失败状态,则返回一个失败状态的Promise对象,数据为装有所有Promise的失败原因的数组
  • 存在Promise的运行状态仍为挂起状态(pending),则返回一个Pending状态的Promise对象

Promise.allSettled([Promise对象数组])

有两种情况

  • 全部Promise对象运行结果为已决阶段,则返回一个成功状态的Promise对象,数据为装有所有Promise对象运行结果的状态和数据的数组,单个数据格式为{ status:'xxx',value:... }
  • 存在Promise对象仍为Pending状态,则返回一个Pending状态的Promise对象

Promise.race([Promise对象数组])

返回一个Promise对象,状态和值由最先转变为已决状态的Promise对象确定。

async/await

官方规定的async/await语法

作用:避免了回调函数的使用

async

  • async修饰的函数为一个异步函数,返回一个Promise
  • 标记了async的函数就可以像Promise一样使用
  • 函数return 的值相当于Promise中resolve(data)中的data

await

  • await 表示等待一个异步任务完成,也就是等待一个Promise执行完毕
  • 如果await修饰的不是异步任务,则会把它变成一个异步任务
  • 由于await修饰的是一个异步函数,则await必须用在async修饰的函数中
  • await避免了回调函数
async function m(){
    return 123; // 相当于返回一个完成状态的Promise,resolve(data)中的data为123
}

// 使用await搭配
async functoin test(){
    const res = await m();
    console.log(res);
}
//等价于
asycn function test_ (){
    m().then((data)=>{
        console.log(data);
    })
}

经过我的分析:

await避免回调的原理就是,await会等待其修饰的Promise对象,其修饰的Promise会返回一个值,值为resolve(data)中的data,理论上来讲如果失败则值为reject(reason)中的reason,但是await只是等待Promise对象的完成,如果失败即reject(reason)/抛错 则需要做失败相应处理catch 可以使用try-catch语句 以避免回调,并且使得异步任务的处理变得优雅直观

try{
    const reason = '不告诉你为什么'
    const res = await Promise.reject(reason);
}catch(err){
    console.log(err)
}
// >> 不告诉你为什么

OK,到此呢,Promise、async/await就告一段落了 Promise 两个阶段,三个状态,resolve/reject将Promise对象从未决阶段转为已决阶段的相应状态。

Promise 链式调用

.then(callback)进行完成状态的后续处理, .catch(callback)进行失败状态的后续处理,

它们都会返回一个Promise,

Promise的状态和值由return 决定,

  • 如果return一个普通值则状态是完成状态
  • 如果return一个Promise按照这个Promise的状态和值
  • 如果抛出错误比如使用throw,那么状态为失败状态rejected,数据为错误信息

在Promise的链式调用链中,如果后续处理不是相应状态的后续处理,则那部分后续处理会类似于被跳过,只起到一个传递的作用。

Promise的链式调用避免了回调地狱

async/await

  • async修饰的函数为一个异步函数,return返回一个Promise,用async修饰的函数可以当成Promise来使用。
  • await修饰一个异步任务,如果不是异步任务则会转换成异步任务,即转换成Promise,它表示等待所修饰的异步任务完成,其避免回调函数的原因在于能够等待Promise完成然后返回resolve(data)中的data然后我们可以用一个变量接住这个data。来进行后续处理。
  • 如果await修饰的Promise抛错或失败了,则需要用catch处理 所以一般可以把await放在一个try-catch中,这样就完全避免了使用.then().catch()回调。当然并不是必须放try-catch中,因为开发人员可以通过开发者工具看到,而用户不需要看到这个err,他们不是去解决程序问题。
  • 同时一个重点是:由于await修饰的是一个异步任务,所以await一定要放在一个async修饰的函数中。 async/await避免了回调函数。

OK,Promise和async/await的总结就到这里了,我是LC_Happy祝好!

这里也感谢渡一的课程了