1,先看看经典面试题
先贴一个经典面试题,执行结果可能跟具体的chrome版本有点不同。
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((resolve) => {
console.log('promise0');
resolve();
})
.then(() => {
console.log('promise1');
})
.then(() => {
console.log('promise2');
});
console.log('script end');
执行结果:
script start
async1 start
async2
promise0
script end
async1 end
promise1
promise2
setTimeout
分析这段代码的基础是js事件的宏任务和微任务队列,以及promise async/await的使用。
1,首先js的线程从头开始一步步执行同步代码,执行打印:
script start
async1 start
3,然后执行遇到await async2(),一般来说遇到awit的时候,会让出当前线程,但是await的执行顺序是从右向左执行的过程,所以会优先执行async2函数的,打印:
async2
然后返回一个promise对象,async2函数返回之后,遇到await之后,会将promise对象放到微任务队列中,那么这个promise对象到底是什么呢?想一想我们平时执 new Promise().then(res=>{})一般都是promise返回一个不是pending的状态之后,会讲then里的res=>{}对应函数放到微任务队列中去,那么async2执行之后,就相当于返回一个值为undefined的promise对象,那么,此刻就相当于把(undefined)=>{resolved(unfined)}函数放到一个微任务队列中,然后await让出主线程。继续之后之后的同步代码。
4,Promise中的代码也是同步代码,所以会打印,
promise0
因为立即执行了resolve,,随意此刻Promise的状态是resoled状态,他的then函数会会放到微任务队列等到之后执行,也就是将() => {console.log('promise1');}放到微任务队列
5,最后执行console.log('script end');,打印:
script end
到此,所有的同步代码已经完成。
6,js线程开始去取微任务队列中的任务执行,async执行之后放进去的(undefined)=>{resolved(unfined)}会得到执行,await等待结果返回,会执行之后的代码,于是打印:
async1 end
7,接着执行下一个微任务:打印:
promise1
由于promise1执行之后,还是会返回rosloved状态的promise,随意此时,这个promise的then函数,也就是() => {console.log('promise2'); }会放到微任务队列,,js线程再次取微任务的时候,执行打印:
promise2
8,所有的微任务都执行完成之后,会去取宏任务队列依次执行,打印:
setTimeout
稍微变异一下
async function async1() {
console.log('async1 start');
await async2()
.then((res) => {
console.log('res1');
})
.then((res) => {
console.log('res2');
});
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise((resolve) => {
console.log('promise0');
resolve();
})
.then(() => {
console.log('promise1');
})
.then(() => {
console.log('promise2');
});
console.log('script end');
这段代码相对于上面的代码,只是在async1 的 await的函数中多加了两个then函数,我们来看一下执行结果:
script start
async1 start
async2
promise0
script end
res1
promise1
res2
promise2
async1 end
setTimeout
在上面的经典题中可能比较难理解的还是async1 end和promise1的顺序,我们借着这个变题再深刻理解一下,async1中的await 后的async2执行完成时候,其实是将res1的函数放到异步队列中去了,而之前没有定义async2()的then的时候,其实就相当于把(undefined)=>{resolved(undefined)}当成一个微任务。理清了这个逻辑,那么接下来的执行顺序应该都能清楚了。