async / await 作用
async/await 是 Promise 的语法糖(await 是 then 的语法糖),通过同步的方式执行异步任务。
以下用一个对比来说明以下
现在我们要实现一个红绿灯的效果,通过异步任务依次输出 绿、黄、红 (时间的模拟暂时不考虑) 如果用传统的 Promise 实现:
Promise.resolve('绿').then((res) => {
console.log(res)
Promise.resolve('黄').then((res) => {
console.log(res)
Promise.resolve('红').then((res) => {
console.log(res)
})
})
})
我们可以用 async/await 来实现一下:
// 立即执行函数
(async () => {
await Promise.resolve('绿').then(res => console.log(res))
await Promise.resolve('黄').then(res => console.log(res))
await Promise.resolve('红').then(res => console.log(res))
})()
通过前后对比,我们可以发现,在需要异步任务按照顺序严格执行的情况下, async/await 可以避免嵌套过多的情况,取而代之的是简单易懂的同步形式代码。
async / await 基础使用
- async 表示这是一个 async 函数(即普通函数变成了异步函数), await 只能用在 async 函数里面,不能单独使用
// 单独使用 await 报错 (function(){ await Promise.resolve('Jasmine') })() // Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
- async 返回的是 Promise 成功的对象,await 就是等待这个 promise 的返回结果后,再继续执行(这句怎么理解?执行顺序里解释)。有无 resolve,取决于函数中有无 return 值。
-
// 在函数中没有 return 值,故无 resolve (async function(){ await Promise.resolve('Jasmine') })() // Promise {<fulfilled>: undefined} // 在函数中有 return 值,故有 resolve (async function(){ return await Promise.resolve('Jasmine') })() // 888 // Promise {<fulfilled>: 'Jasmine'}
- await 等待的是一个 Promise 对象,后面必须跟一个 Promise 对象,但是不必写 then(),直接就可以得到返回值,如果不是,则会包裹一层 Promise.resolve()
// await 后面不是 Promise,则会包裹一层 Promise.resolve() (async function(){ return await 666 })() // Promise {<fulfilled>: 666}
如果发生异常,怎么处理?
可以使用 try/catch 来捕获异常,把 await 放到 try 中进行执行,如有异常,就使用 catch 进行处理。
async function myFunction() {
try {
await somethingThatReturnAPromise()
} catch (err){
console.log(err)
}
}
//另一种写法
async function myFunction() {
await somethingThatReturnAPromise().catch(function(err) {
console.log(err)
})
}
async / await 存在时的执行顺序
async function 内部的 await 表达式之后的代码块会在下一个微任务中执行
案例 1
(async () => {
setTimeout(() => {
console.log('after 0s')
}, 0)
new Promise((resolve) => {
resolve('我是微任务啦')
}).then((res) => {
console.log(res)
})
console.log('我是同步任务哎')
await new Promise((resolve) => {
resolve("我成功喽")
}).then((res) => {
console.log(res)
});
new Promise((resolve) => {
resolve('我是await之后的微任务啦')
}).then((res) => {
console.log(res)
})
console.log('我是await之后的同步任务啦')
})()
// 我是同步任务哎
// 我是微任务啦
// 我成功喽
// 我是await之后的同步任务啦
// 我是await之后的微任务啦
// after 0s
执行顺序如上注释。我们用事件循环机制来分析
按照以上描述,事件循环队列中的任务如下:
案例 2
将案例1改造一下,await new Promise 里面包个定时器
(async () => {
setTimeout(() => {
console.log('after 0s')
}, 0)
new Promise((resolve) => {
resolve('我是微任务啦')
}).then((res) => {
console.log(res)
})
console.log('我是同步任务哎')
await new Promise((resolve) => {
setTimeout(function(){
resolve("我成功喽")
}, 0)
}).then((res) => {
console.log(res)
});
new Promise((resolve) => {
resolve('我是await之后的微任务啦')
}).then((res) => {
console.log(res)
})
console.log('我是await之后的同步任务啦')
})()
// 我是同步任务哎
// 我是微任务啦
// after 0s
// 我成功喽
// 我是await之后的同步任务啦
// 我是await之后的微任务啦
执行顺序如上注释。事件循环队列中的任务变为如下:
总结
- 当遇到 await,会先暂停 await 及后边代码的执行,直到 Promise 的状态发生改变后,才会继续执行 await 以及后边的任务
- await 本质是 then 的语法糖,其实是个微任务
- 在 await new Promise 中如果包含一个定时器,定时器的回调函数中写 resolve() 或者reject(),那么这个定时器是个宏任务,会在宏任务队列排队完成后,再改变 Promise 的状态,然后 await 才能执行,再取消阻塞