一文了解async await异步函数

662 阅读4分钟

上一篇我们说到promise,当然除了promise对象我们ES7也推出了async await新的异步任务解决方案,这篇文章我们就一起认识一下async await,以及它出现的原因。

ES7新特性 async await

为什么有async await?

大家都知道,对于异步任务的解决方案我们已经有了Promise的解决方案了,那为什么我们还需要async await这种解决方案呢? 其实原因非常简单,当代码中存在大量的异步请求的时候,Promise的链式调用看起来就没那么直观了,当满屏幕充斥着大量的.then方法时,相信大家也会晕头转向的,这就是async await这个语法糖出现的原因。

async await需要注意什么?

  • async和await是一对好基友,他们两个缺一不可,算是Promise的进化版本
  • async和await也是为Promise服务的
  • async声明的必须是一个function,不然await会报错
  • await是使用在async声明的函数内部的

async的本质

  • async本质是它声明的函数会返回一个Promise
async function(){
    return ("i am Promise")
}()

这个函数的返回为Promise(<resolved> : "i am Promise")
这个会自动的解析成Promise.resolve("i am Promise")

等同于下面这种写法
(async function () {
    return Promise.resolve("i am Promise");
})()

所以如果当你想用async声明函数的返回值时,可以用变量存储这个async声明的函数
然后通过链式调用.then()来拿到所返回的Promise传来的值

const asyncFun = async function(){
    return Promise.resolve("i am Promise")
}
asyncFun.then((result)=>{
    console.log(result)
})

结果显示输出 "i am Promise"

await的使用

  • await字面意思就是"等一小会儿"的意思,他可以提供等待异步返回的能力,只要await声明的异步任务还没有返回,后面的程序将不会执行

  • await一定是在等待一个Promise对象的返回,如果await等待返回的对象不是Promise的话,它是起不到等待作用的

看一下这个例子

const asyncFun = function(){
    let result = async setTimeout(()=>{
        console.log("等待1秒")
    },1000)
    console.log("猜猜是谁先执行")
    return result
}

asyncFun.then(result=> {
    console.log("输出",result)
})

输出结果如下:
猜猜是谁先执行
输出 1
等待1

setTimeout是异步可是并没有等待它执行完毕再向后执行,原因是await必须等待一个Promise的返回才能起作用,这个用于异步任务调用的时候。

异步任务调用时Promise和async await的不同

假定现在有两个异步函数

asyncFunBefore = ()=>{
    return new Promise((resolve,reject)=>{
           setTimeout(()=>{
               resolve('延迟1秒')
           },1000)
    })
}

asyncFunAfter = ()=>{
    return new Promise((resolve,reject)=>{
           setTimeout(()=>{
               resolve('延迟2秒')
           },2000)
    })
}

比如我现在需要先延迟1秒,再延迟2秒,再延迟1秒再输出结果,那么Promise的写法如下:

(这里的参数我用不同的序号标识一下,这样大家分辨的时候会更直观一点)

asyncFunBefore()
.then(result =>{
    console.log(result)
    return asyncFunAfter()
}).then(result2 =>{
    console.log(result2)
    return asyncFunBefore()
}).then(result3 =>{
    console.log(result3)
    console.log('完成')
}).catch(error =>{
    console.log(error)
})

这样看起来非常的不直观,如果调用方法多了的话会更加难以理解

接下来我们改造成async await的写法试试:

(async ()=>{
    const result = await asyncFunBefore()
    console.log(result)
    const result2 = await asyncFunAfter()
    console.log(result2)
    const result3 = await asyncFunBefore()
    console.log(result3)
    console.log('完成')
})()

这里我们是不是没展示如何捕获错误,一般我们用try catch方法来做,具体如下:
(async ()=>{
    try{
        const result = await asyncFunBefore()
        console.log(result)
        const result2 = await asyncFunAfter()
        console.log(result2)
        const result3 = await asyncFunBefore()
        console.log(result3)
        console.log('完成')
    }catch(e){
        console.log(e)  //这里用来捕获错误
    }
})()

通常代码中不会存在太多的try catch,如果try catch太多的话,可能代码需要重构一下

async await中断

  • 首先明确一下,Promise一旦状态改变是无法再被终止的
  • 这里的中止是将方法挂起,并没有取消Promise的请求
  • 直接在async声明的函数中返回一个值即可,null或者false都是可以的

代码如下所示:

let i = 6
const asyncFun = ()=>{
        const result = await asyncFunBefore()
        console.log(result)
        const result2 = await asyncFunAfter()
        console.log(result2)
        const result3 = await asyncFunBefore()
        if(i > 5){
            return '从这里退出,后边不会执行'
        }
        console.log(result3)
        console.log('完成')
}

asyncFun.then(result=> {
    console.log(result)
}).catch(error=> {
    console.log(error)
})

这里会输出:
从这里退出,后边不会执行

原因是我们之前说了,async声明的函数本质是返回一个Promise,
所以在这里return就相当于是Promise.resolve('从这里退出,后边不会执行')
这个时候状态发生改变了,会直接去走.then()中的代码
这样就可以手动终端async中的后续代码,但是只是暂时挂起,并不是取消请求了