我们来看一道面试题:
<script>
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(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
</script>
结果
分析
浏览器是多线程的,JS是单线程的 => 浏览器只给了其一个线程来渲染。
创建函数 async1
创建函数 async2
=> "script start"
设置一个定时器(宏任务A)
函数执行
=> "async1 start"
await async2();执行async2等待返回的结果(微任务B)
=>"async2"
new Promise的时候会立即把EC函数执行(new的时候是同步的)
=> "promise1"
resolve()/reject()微任务 C
=> "script end" 主栈第一阶段完成
事件队列 Event Queue
微任务
B:等待async2的结果,有结果再执行下面代码
C:resolve/reject执行的时候把then/catch中方法执行
宏任务
A:xxms执行xx
Event Loop 事件循环
- 主线程运行的时候会生成堆(heap)和栈(stack);
- js从上到下解析方法,将其中的同步任务按照执行顺序排列到执行栈中;
- 当程序调用外部的API时,比如ajax、setTimeout等,会将此类异步任务挂起,继续执行执行栈中的任务,等异步任务返回结果后,再按照执行顺序排列到事件队列中;
- 主线程先将执行栈中的同步任务清空,然后检查事件队列中是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务排列到事件队列中。
- 主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个过程是循环往复的... ...,这个过程被称为“Event Loop 事件循环”。
微任务执行完执行宏任务
=> B "async1 end"
=> C "promise2"
=> A "setTimeout"
微任务:promise,async,await
宏任务:setInterval,setTimeOut,事件绑定