持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
一、async 和 await 的作用
ES8新增了 async/await 提供了异步编程解决方案
1. async
async用来声明异步函数,使用async关键字可以让函数具有异步特征,但是在总体上代码依然是同步行为,与普通函数没有区别,但是在返回值上与普通函数不同。
- 代码示例
async function foo(){
return ''
}
const result = foo()
console.log(result)
-
打印结果
-
小结
- 从上面打印结果可以看出来被
async修饰的函数返回的是一个promise对象,并且是成功状态 - 被
async声明的函数直接return结果数据,async会把这个返回的数据通过Promise.resolve()封装成Promise对象 - 当然,如果async声明的函数
return为空的话,返回的对象为undefined
2. await
因为异步函数主要针对不会马上完成的任务,所以需要await关键字来暂停异步代码的执行,需要注意的是await会暂停执行异步函数后面的代码。
3. 关于async和await的综合使用
async/await在使用的时候起作用的是await。因为async只是让函数具有异特征,它并不会去改变代码的执行,await才是关键。就如同开始就讲的,没有await,async修饰的函数就是普通函数,明白这一点很重要。
- 代码示例
//async修饰
async function foo1(){
console.log('foo1')
}
//async/await同时用
async function foo2(){
console.log(await Promise.resolve('foo2'))
}
//普通函数
function foo3(){
console.log('foo3')
}
foo1()
foo2()
foo3()
- 打印结果
- 小结
从上面的打印顺序就能看出来,被
async修饰的函数第一个调用第一个执行,async/await同时修饰的则是暂停执行。
4. 关于await的进一步拓展
await也并不是完全等待,也不是说函数内部只要有await,整个函数或者在之后的函数都会暂停,而是等await后面的值什么时候可以用了,JavaScript运行时会向消息队列推送一个任务,这个任务会恢复异步函数的执行。在函数里没有await修饰的位置,就还是同步行为。这一整个流程关乎同步异步问题,通过一个简单的例子来看看这一点。
- 代码示例
//被async修饰
async function foo(){
console.log(2)
await null
console.log(4)
}
console.log(1)
foo()
console.log(3)
- 打印结果
- 小结
同步打印出
1,调用函数输出2,await null导致函数退出然后等3打印最后才打印4.所以也能看出来即使是立即输出4,也会被暂停,只有被await修饰后的代码才能被暂停。
二、任务队列
通过了解了async、await的前置知识后,再来说说任务队列,这样会比较好理解。
1. 关于任务队列,你必须要知道的
- JS是单线程编程语言,同一时间只能做同一件事情
- JS分为同步任务和异步任务
- 同步会阻塞代码执行,异步不会阻塞代码执行
- 任务又分为宏任务和微任务,而他们分别有相对应的队列:宏任务队列(macro tasks)和微任务队列(micro tasks)而宏任务队列可以有多个,微任务队列只能有一个
宏任务、微任务
- 宏任务
- script(全局任务), setTimeout, setInterval, Ajax, DOM事件监听,setImmediate(Node), I/O, UI rendering.
- 微任务
- Promise.then,async/await,mutationobserver(H5),process.nextTick(Node)
2. 具体执行机制
具体的执行还得看代码验证一下
1. 代码
下述打印题共输出8个结果,可以试着输出一下
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
async1()
new Promise(function(resolve) {
console.log('promise1')
resolve()
}).then(function() {
console.log('promise2')
})
console.log('script end')
2. 分析
- 首先从上往下开始也就是事件循环从宏任务开始,打印
script start - 遇到
setTimeout,其作为一个宏任务源,则会先将其任务分发到对应的队列中 - 接下来调用函数
async1(),进入async1,首先打印async1 start,遇到了await时,会将await后面的表达式执行一遍,所以就紧接着输出async2,而await后面的打印console.log('async1 end')则会被异步处理,加入到Promise的微队列中 - 接着跳出函数继续往下执行后面的代码,遇到
new Promise,因为async/await是new Promise的语法糖,async相当于new Proimise所以Promise中的函数是立即执行的,所以接着打印promise1,而await相当于.then(),所以.then()加入微任务的异步队列 - 跳出函数,接着执行完
console.log('script end')全局任务也就执行完了 - 当执行完一个宏任务之后都会去检查里面是否有微任务未执行,所以开始清空微队列,按顺序输出微队列里面的
async1 end,promise2,setTimeout - 当微任务全部执行完毕之后,就会开始执行下一个宏任务,而下一个宏任务是
setTimeout,只有他一个,故此输出setTimeout,至此,整个流程全部结束
3. 打印结果如下
好了,以上就是本篇文章的分享,感谢阅读!