本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
async 函数是使用async
关键字声明的函数。 async 函数是[AsyncFunction
]构造函数的实例, 并且其中允许使用await
关键字。async
和await
关键字让我们可以用一种更简洁的方式写出基于[Promise
]的异步行为,而无需刻意地链式调用promise
。
async
async关键字用于声明异步函数,它可以在函数声明、函数表达式还有箭头函数中使用
1. async function Async(){}
2. let Async = async function(){}
3. let Async = async ()=>{}
await
不能够单独出现,其函数前面一定要有 async
,所以实际使用时 async
和 await
要结合起来使用。
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在底层转化为 Promise 和 then 回调函数,也就是说这是 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 是“异步”的简写, await 是 async 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 的位置执行返回的 Promise 的 resolve 函数,再将 resolve 放入微任务列表中,打印出 'async1 end' ; -当微任务队列为空时,执行宏任务,打印出 'setTimeout'。
输出结果: // start 、async2 end 、Promise 、end 、Promise1 、Promise2 、async1 end 、setTimeout
(73版本之后)新款浏览器为 await 开辟了特别通道,执行更前了(相当于同步代码执行),所以当调用 async1()
时await直接提前到当前执行任务直接打印出'async2 end'
。
结语
以上就是小编对于异步函数async/await的一些个人的见解,希望能够帮助到正在学习了解事件循环的执行顺序,若大佬们发现纰漏望各位指正,大家共同进步,本系列文章都是个人在学习当中的见解。