前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
async await详解
原理:
async声明该函数是异步的,且该函数会返回一个promise。
await必须放在async函数中使用
- await+Promise
这是最常见的场景,await 会等待Promise的状态改为fullfilled
,如果成功,那么会将async函数剩余任务推入到微任务队列,如果失败,那么剩余任务不会被推入微任务队列执行,它会返回Promise.reject(err) - await + 普通的值
即使await右边非函数,只是一个普通的数值,但它本质上是将其转化为Promise.resolve(2)
,所以会返回一个成功的promise
因此,当await等待到了成功的结果后,它会将async函数剩余内容推入到微任务队列中等待执行。
async function run() { console.log('start 1') const res = await 2 console.log(res) console.log('end') } run() console.log('3')
- await+函数
一个最简单的场景:
function fn() { console.log('fn start') console.log('fn end') } async function run() { console.log('start 1') const res = await fn() console.log(res) console.log('end') } run() console.log('3')
运行结果为:
结论:如果await 右边是一个
函数
,它会立刻执行这个函数,而且只有当这个函数执行结束后(即函数完成)!才会将async剩余任务推入微任务队列
**这是因为await等待的永远是promise,如果函数返回undefined,那么await会等待函数执行完成
,将返回值转化为Promise.resolve(undefined)
**(即第二种情况 await + 普通值)。如果函数执行完成后,返回一个失败的promise,那么它将不会再把剩余任务推入到微任务队列。
复杂点的案例,如果fn函数里面嵌套了await
案例:
async function async1() { console.log(1) await async2() console.log(2) } const async2 = async () => { await setTimeout((_) => { Promise.resolve().then((_) => { console.log(3) }) console.log(4) }, 0) } const async3 = async () => { Promise.resolve().then(() => { console.log(6) }) } async1() console.log(7) async3()
分析:
- 首先调用async1 输出1 ,await async2()立刻调用async2(),直至async2函数完成后,才会将cl(2)推入微任务队列
- 调用async2(), await 定时器,(定时器本身也是个函数),因此先将定时器的回调函数推入宏任务队列,定时器本身返回一个定时器ID
因此,async2可以转化为:
async ()=>{ await 数值; //即第二种情况 }
- 重点:await 数值会转化为await Promise.resolve(数值),再将
async函数中剩余任务推入到微任务队列执行
。这时候,async2函数中的剩余任务还有个return undefined
,这代表async2函数并不能立刻执行完毕
,会将return undefined推入到微任务队列中
(这才代表着async2函数真正执行结束
)
目前:宏任务队列:定时器回调函数任务
微任务队列:return undefined(async2函数执行完毕)
- 回到开始,await async2(),目前async2还没有执行结束,因此调用cl(7)
- 调用async3(),微任务队列推入 cl(6)
目前:宏任务队列:定时器回调函数任务
微任务队列:return undefined(async2函数执行完毕) cl(6)
- 从微任务队列中取出第一个任务,return undefined,
async2()函数执行完毕,await async2()
转化为 Promise.resolve(undefined),因此将cl(2) 推入微任务队列
所以真正的结果是:1 7 6 2 4 3
案例2:
async function async1() { console.log(1) await async2() console.log(2) } const async2 = async () => { await (async () => { await (() => { console.log(3) })() console.log(4) })() } const async3 = async () => { Promise.resolve().then(() => { console.log(6) }) } async1() console.log(7) async3()
思路:
- 首先跟开始一样,调用async1() 输出1,遇到await async2(),进入等待状态,等待async2()函数完成
- 调用async2函数,遇到
await 立即执行函数1
,立即执行立即执行函数1,又遇到了await 立即执行函数2
,继续执行立即执行函数2,输出3。同时立即执行函数2没有返回值,等同于return undefined。因此可以转化为
await Promise.resolve(undefined)
- 等待到了成功的promise,将
cl(4)和立即执行函数1的返回值结果(return undefined)即立即执行函数1完成 推入到微任务队列
。
目前:宏任务队列:
微任务队列:cl(4) return undefined(即立即执行函数1完成)
- 因为async2函数内部
await 立即执行函数1
,所以它需要等待立即执行函数1完成
后,将async2函数执行完成推入微任务队列,再将cl(2)推入微任务队列 - 输出7
- 调用async3 ,将cl(6)推入微任务队列
目前:宏任务队列:
微任务队列:cl(4) return undefined(即立即执行函数1完成) cl(6)
- 同步任务完成,调用微任务队列任务,输出4,立即执行函数1完成,将async2完成推入微任务队列,将cl(2)推入微任务队列。
目前:宏任务队列:
微任务队列: cl(6) async2完成 cl(2)
所以最后的结果是:1 3 7 4 6 2。
前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库