异步函数-async/await函数

1,533 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

async 函数是使用async关键字声明的函数。 async 函数是[AsyncFunction]构造函数的实例, 并且其中允许使用await关键字。asyncawait关键字让我们可以用一种更简洁的方式写出基于[Promise]的异步行为,而无需刻意地链式调用promise

async

async关键字用于声明异步函数,它可以在函数声明、函数表达式还有箭头函数中使用

 1.   async function Async(){}
    
 2.   let Async = async function(){}
    
 3.   let Async = async ()=>{}

await 不能够单独出现,其函数前面一定要有 async ,所以实际使用时 asyncawait 要结合起来使用。

await

当我们使用async 声明了一个异步函数,那么我们就可以在这个异步函数内部使用await关键字。

function getJSON(){
   return new Promise((resolve, reject) => {
       setTimeout(() =>{
       console.log('JSON');
       resolve('ok');
   },500)
   })
}

async function testAsync(){
   await getJSON();  
   console.log('数据拿到了');
}

testAsync()  // 打印结果为‘JSON’  ‘数据拿到了’

其实await关键字就做了两件事:

1. 将写在 await 后面的代码放在 async 创建的那个 Promise 里面去执行;
2. 将写在 await 下面的代码放到前一个 创建的 Promise 对象的 .then 里面去执行。

await 返回的也是Promise对象,它只是把await下面的代码放到了 await 返回的Promise的.then里面执行.

在分析下面的代码之前我们先要理解async/await在底层转化为 Promisethen 回调函数,也就是说这是 Promise 的语法糖。 (对promise不了解的小伙伴可以点开学习promise);

async function getAsyncContent() {
    console.log('getAsyncContent');
    return 1
}
相当于
function getAsyncContent(){
    return Promise.resolve().then(() =>{
        console.log('getAsyncContent');
    return 1
    })
}
// async === new Promise,在函数前面加一个async就相当于return了一个new Promise
async function test() {
    let a = 2
    let c = 1
    await getContent()
    let d = 3
    await getPromise()
    let e = 4
    await getAsyncContent()
    return 2
}
相当于
function test() {
    return Promise.resolve().then(() => {
        let a = 2
        let c = 1
        return getContent()
    })
        .then(() => {
            let d = 3
            return getPromise()
        })
        .then(() => {
            let e = 4
            return getAsyncContent()
        })
        .then(() => {
            return 2
        })
}
// await 后面也要接一个Promise对象(等待一个Promise对象)

每次我们使用 await ,解释器都创建了一个 Promise 对象,把剩下的async函数中的操作都放到 then 回调函数中。async/await 的实现,离不开 Promise 。从字面意思来理解 async 是“异步”的简写, awaitasync await 的简写,可以理解为等待异步执行结束之后再执行。

让我们来分析一段经典的例子,来好好体会一下async函数的事件执行顺序:

console.log('start');   

async function async1() {

  await async2() 
  
  console.log('async1 end'); 
}


async function async2() {
  console.log('async2 end');
} 

async1()

setTimeout(() => {
  console.log('setTimeout');
}, 0)

new Promise(resolve => {
  console.log('Promise'); 
  resolve()
})
  .then(() => {
    console.log('Promise1');
  })
  .then(() => {
    console.log('Promise2'); 
  })
console.log('end');

我们一起来细细品味这段代码:

  • 首先从上至下先执行,打印出 'start' ,调用 async1() ,此时返回一个 Promise ,所以打印出 'async2 end'
  • 每个 await 会产生新的 Promise ,但是这个过程本身是异步的,所以await 后面的 async2() 不会立即调用;
  • 继续执行同步代码,打印出 'Promise''end',此时同步代码已经执行结束了,检查是否有异步代码需要执行,将 .then 回调函数放入微任务列表中等待执行;
  • 检查微任务列表是否为空,若不为空开始执行微任务,按照先入先出的规则执行微任务列表,
  • 然后执行打印出 'Promise1' ,此时又有 .then的链式调用,又放入微任务列表中,再次打印出 'promise2';
  • 再回到 await 的位置执行返回的 Promiseresolve 函数,再将 resolve 放入微任务列表中,打印出 'async1 end' ; -当微任务队列为空时,执行宏任务,打印出 'setTimeout'

输出结果: // start 、async2 end 、Promise 、end 、Promise1 、Promise2 、async1 end 、setTimeout

image.png

(73版本之后)新款浏览器为 await 开辟了特别通道,执行更前了(相当于同步代码执行),所以当调用 async1()时await直接提前到当前执行任务直接打印出'async2 end'

结语

以上就是小编对于异步函数async/await的一些个人的见解,希望能够帮助到正在学习了解事件循环的执行顺序,若大佬们发现纰漏望各位指正,大家共同进步,本系列文章都是个人在学习当中的见解。