前言
- 面试的时候经常碰到一些关于js执行顺序的题,涉及到Event-loop,微任务、宏任务、任务队列之类的,有的题比较简单,前段时间在交流群里,群友贴出来一题,我觉得蛮有意思,当时做了一下,给出了简单的解析 ,事后回想,解析的比较笼统,今天我们来扒衣扒皮,来我们直接上题,
上代码
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
},0)
new Promise(resolve => {
console.log('promise')
resolve()
}).then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
-
这题乍一看很长啊,让我们来逐行解析一下(执行环境google浏览器)
-
当时在群里解析的答案: 首先立即执行的,script start 然后调用async1 输出 async2 end ,async1里面的console在await之后,微任务,挂起, 然后碰到setti宏任务,挂起,然后执行new promise的立即执行 console输出promise,。then为微任务,挂起,然后执行script end, 前面挂起了三个微任务,执行顺序为先进先出,saync1 end promise1 promise2 , 最后执行当前的宏任务 settimeout
-
你觉得有哪里解释的不够清晰,涉及到的知识点,微任务,宏任务的执行顺序 我们来拆解一下这题
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
},0)
new Promise(resolve => {
console.log('promise)
resolve()
}).then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
这样写,我觉得大多数了解js运行机制的掘金炮友都知道答案,也基本上都能解释出来(这里不着重讲微任务宏任务,详情看掘金其他炮友的文章,都写的很好)。
// 控制台输出
script start
promise
script end
promise1
promise2
setTimeout
有意思async、await
- 我觉得这里有意思的点是async 和await 的执行顺序,以及执行的里面的一些规则,所以我把他单独拿出来解析,
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
控制台输出:
async2 end
async1 end
-
如果把这个加到上面的拆解片段中,我想大部分人基本上都能猜出答案,但是别人要问你为什么,你想想你该怎么回答,关于这道题我总结了几个为什么,
-
async await 的执行规则是什么样的?
-
await 后面的代码是怎么执行的?
-
await 后面,如果是同步执行的代码,和异步执行的代码,对面代码执行有什么影响?
-
为什么await后面的代码,会被执行什么操作?
-
-
带着问题我们来找答案!(尽量简洁)
- 函数前面加上async时,当我在这个函数调用的时候进行打印发现它输出的是一个promise对象,其实这个函数的本质就是隐式返回了一个promise对象作为结果的函数
- 如果你返回的不是一个promise,JavaScript也会自动把这个值"包装"成Promise的resolve值
- 同时,在单独使用aysnc时,函数里面加上return,和不加上return,返回值也会有差别,果你写了return,那么return的值就会作为你成功的时候传入的值上代码(看输出的Promise{:的值}):
- 在这个函数里里我们加上await后,也是我们平时项目里面常用的,即使调用的是异步代码,它也会变成类似于同步,只有让这个异步代码执行完后,才会执行下面的同步代码来执行。注意一点不能单独使用await,不然就爆红了。
- await会“阻塞”后面的函数,也就是console.log('async1 end')(下一行的代码)
- 那么可以简单理解为,await后面的函数执行完毕时,await会产生一个微任务(也就是为什么会阻塞了)。但是我们要注意这个微任务产生的时机,它是执行完await之后,回调函数会被压入microtask队列,也就是console.log('async1 end'),然后直接跳出async函数,执行其他代码。其他代码执行完毕后,再回到async函数里面去执行剩下的代码
- 结束,基本上上面题目的的答案也明了了,结合微任务宏任务的执行机制,顶部的题也就做出来了,说的相对简洁,如有不到之处,评论区交流!
结语
-
再来一题:(答案,copy到当前控制台,看看有没有整对)
console.log('script start') async function async1() { await async2() console.log('async1 end') } async function async2() { console.log('async2 end') setTimeout(function() { console.log('setTimeout1') },0) return new Promise(resolve => { console.log('promise3') resolve() }).then(function() { console.log('promise4') }) } async1() setTimeout(function() { console.log('setTimeout') },0) new Promise(resolve => { console.log('promise') resolve() }).then(function() { console.log('promise1') }).then(function() { console.log('promise2') }) console.log('script end')