【js】分析一道涉及JavaScript运行机制的题

引言

无意中看到这样一道题,问最后结果是什么?由此分析并记录一下这道题的执行过程以及结果。

async function a1 () {
    console.log('a1 start')
    await a2()
    console.log('a1 end')
}
async function a2 () {
    console.log('a2')
}

console.log('script start')

setTimeout(() => {
    console.log('setTimeout')
}, 0)

Promise.resolve().then(() => {
    console.log('promise1')
})

a1()

let promise2 = new Promise((resolve) => {
    resolve('promise2.then')
    console.log('promise2')
})

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})
console.log('script end')

一轮分析

一、首先执行console.log('script start'),这是毋庸置疑的。

二、接下来,有一个setTimeout方法,但setTimeout是一个macrotask(宏任务,即任务队列,当同步任务执行完了才会去读取任务队列),setTimeout要等同步任务和现有任务队列事件执行完才执行。所以 console.log('setTimeout')被放到了任务队列的末尾。

三、Promise本身是一个同步的立即执行函数,但promise.then()是一个microtask(微任务),故console.log('promise1')放入microtask的第一个。

四、执行a1(),由于async/await的本质是基于Promise的封装,而async返回的是Promise对象,故以下代码:

async function a1 () {
    console.log('a1 start')
    await a2()
    console.log('a1 end')
}

等价于

async function a1 () {
	console.log('a1 start');
	Promise.resolve(a2,then( ()=>{
		console.log('a1 end');
	}) )
}

这样就可以知道console.log('a1 start');执行;然后执行async2(),即console.log('a2'),然后啊a1()中的await后面的代码即console.log('a1 end')就会被放到microtask的第二个。

五、接下来到下面这段代码了:

let promise2 = new Promise((resolve) => {
    resolve('promise2.then')
    console.log('promise2')
})

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})

可以看到,定义了一个Promise函数promise2,并且resolve带了参数promise2.then,这个参数可以在then的回调函数中取得,即res。这里先执行console.log('promise2'),然后再将then里的回调部分放入microtask的第三个。

六、最后执行console.log('script end')

二轮分析

通过一轮分析,我们暂时可以得到结果script start -> a1 start -> a2 -> promise2 -> script end -> …;

也就是说选择所有同步任务已经执行完毕,开始读取任务队列。

由于队列是先进先出的,故microtask按顺序执行(上面有备注第一个/第二个/第三个),即 promise1 -> a1 end -> …

第三个长这样,还得进行分析:

console.log(res)
Promise.resolve().then(() => {
    console.log('promise3')
})

执行console.log(res),其中res为resolve传的参数promise2.then;然后又有一个promise.then,故放入microtask。但此时microtask没有其他任务,故执行console.log('promise3')

所以二轮结束后,结果应该是这样的:
script start -> a1 start -> a2 -> promise2 -> script end -> promise1 -> a1 end -> promise2.then -> promise3 -> …

三轮分析

任务队列中的microtask已经执行,且没有其他要执行的任务,故最后执行setTimeout,即console.log('setTimeout')

三轮结束后,得到完整结果:
script start ->
a1 start ->
a2 ->
promise2 ->
script end ->
promise1 ->
a1 end ->
promise2.then ->
promise3 ->
setTimeout

结语

这样类似的题目很多,但重要地是掌握其中的知识点。如果以上内容有表达不正确的地方欢迎指出并探讨!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值