Promise以及async和await

301 阅读5分钟

为什么要有Promise

因为回调地狱,多层嵌套的callback会让基本超过五层嵌套的代码难以理解,并且难以维护和重构,所以出现了Promise

Promise

Promise是一个构造函数,接受一个executor函数作为参数,executor函数有两个函数形参resolve,reject,返回一个Promise对象,异步处理成功调用resolve函数,失败则调用reject函数

                                                                                            js
let p = new Promise( function(resolve, reject) {
    //异步处理
    //然后执行resolve或者reject
} )

resolve和reject就像两个预案,成功了就执行resolve方案,失败了就执行reject方案

一个 Promise有以下几种状态:

pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。

当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。

当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

  • 如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
  • 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
  • 如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
  • 如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
  • 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。

resolve(成功) onFulfilled会被调用

                                                                                            js
let promise = new Promise((resolve, reject) => {
   resolve('fulfilled') // 状态由 pending => fulfilled
});
promise.then(result => { // onFulfilled
    console.log(result) // 'fulfilled' 
}, reason => { 
    // onRejected 不会被调用
})


reject(失败) onRejected会被调用

                                                                                            js
let promise = new Promise((resolve, reject) => {
   reject('rejected') // 状态由 pending => rejected
})
promise.then(result => { // onFulfilled 不会被调用
  
}, reason => { // onRejected 
    console.log(reason) // 'rejected'
})

then的执行逻辑

                                                                                            js
var aaa = new Promise((resolve, xxx) => {
  xxx("第一次失败了")
})
aaa.then(
  () => {
    console.log("成功了")
  },
  (error) => {
    console.log(error)      //第一次失败了
    return Promise.reject('第二次又失败了')
  }
).then(()=>{
    
},(error)=>{
    console.log(error)      //第二次又失败了
})

只有当aaa的状态变成rejected也就是aaa里面明确写了reject那么才会返回错误,如果你写的是resolve那么则会返回成功了

promise的API我就不介绍了,基本常用的也就是then,遇到不懂的直接面向mdn编程就好

如果你想彻底消除回调函数

                                                                                            js
p.then(onFulfilled, onRejected).
    then((success)=>{console.log('成功')},(error)=>{console.log('失败')}).then(...,...)....

then方法里面还是有两个回调函数,如果你看它不爽怎么办

await

await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。把异步代码变成同步代码

                                                                                            js
function 等两秒() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('我已经等了2s了')
    }, 2000)
  })
}

//异步写法
等两秒().then(res=>{

})

//同步写法
let success = await 等两秒() 
console.log(sucess)         //我已经等了2s了

await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。

若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
如果你想拿到这个错误

                                                                                            js
try{
    .....
}catch(error){
    console.log(error)
}

另外,如果 await 操作符后的表达式的值不是一个 Promise,那么该值将被转换为一个已正常处理的 Promise。

async

async function foo() {
  return 'hello async'
}

foo()
//Promise {<resolved>: "hello async"}

当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

用不用async和await有什么区别?

先看看普通调用

function foo() {
  return new Promise(resolve => {
    setTimeout(() => resolve('这是一个异步操作'), 1000)
  })
}

foo().then(res => {
  console.log(res)
})

使用async和await

function foo() {
  return new Promise(resolve => {
    setTimeout(() => resolve('这是一个异步操作'), 1000)
  })
}

async function test() {
  let res = await foo()
  console.log(res)
}

test()

单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。