同步和异步任务分别进入不同的执行”场所”,同步的进入主线程(执行栈),异步的进入 Event Table 并注册函数。当指定的事情完成时,Event Table 会将这个函数移入 Event Queue。
主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的 Event Loop(事件循环)。
这里异步任务的 Event Queue 分两种情况的,即宏任务 (macrotask) 和微任务 (microtask),当主线程任务完成为空去 Event Quenu 读取函数的时候,是先读取的微任务,当微任务执行完毕之后,才会继续执行宏任务。
综上事件循环为:同步 > 异步 微任务 > 宏任务
Promise 构造函数是和主线程代码一起同步执行的,then 方法是异步执行的
那么微任务和宏任务都有什么呢,简单总结下就是:
宏任务:整体代码 script,setTimeout,setInterval
微任务:原生 Promise 相关,process.nextTick
await 后的属于异步微任务,执行在 Promise.then 后面
看代码说输出
一:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('start');
setTimeout(() => {
console.log('setTimeout');}, 0);
async1();
new Promise(resolve => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
})
var arr = [];
for (let index = 0; index < 10; index++) {
arr.push(index);
}
console.log(arr);
console.log('end');
答案:
"start"
"async1 start"
"async2"
"promise1"
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"end"
"async1 end"
"promise2"
"setTimeout"
分析:
- 主函数打印”start”。
- 执行 setTimeout,将 setTimeout 回调函数放入异步宏任务队列。
- 调用 async1,依次打印”async1 start”、”async2”,将 await 后面的回调函数放到异步微任务队列。
- 执行 new Promise,输出”promise1”,将 then 里面的回调函数放入异步微任务队列。
- 执行耗时长的循环语句并打印 arr,并且输出”end”,这时候主栈执行完毕为空。
- 将异步微任务队列里的函数按照”先进先出”的顺序依次执行,输出”async1 end”,”promise2”。
- 将异步宏任务队列里的函数按照”先进先出”的顺序依次执行,输出”setTimeOut”。 二、
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
new Promise(function(resolve){
console.log('5');
resolve();
}).then(function(){
console.log('6')
});
setTimeout(function(){
console.log('7')
});
function bar(){
console.log('8')
foo()
}
function foo(){
console.log('9')
}
console.log('10')
bar()
答案:
"2"
"4"
"5"
"10"
"8"
"9"
"3"
"6"
"1"
"7"
分析:
- 首先浏览器执行Js代码由上至下顺序,遇到 setTimeout,把 setTimeout 分发到宏任务 Event Queue 中
- new Promise 属于主线程任务直接执行打印 2
- Promis 下的 then 方法属于微任务,把 then 分到微任务 Event Queue 中
- console.log(‘4’) 属于主线程任务,直接执行打印4
- 又遇到 new Promise 也是直接执行打印 5,Promise 下到 then 分发到微任务 Event Queue中
- 又遇到 setTimouse 也是直接分发到宏任务 Event Queue中,等待执行
- console.log(‘10’) 属于主线程任务直接执行
- 遇到 bar() 函数调用,执行构造函数内到代码,打印 8,在 bar 函数中调用 foo 函数,执行 foo 函数到中代码,打印 9
- 主线程中任务执行完后,就要执行分发到微任务 Event Queue 中代码,实行先进先出,所以依次打印 3,6
- 微任务 Event Queue 中代码执行完,就执行宏任务 Event Queue 中代码,也是先进先出,依次打印 1,7。 三、 (题目来自 2022.3 字节飞书 日常实习 一面,用户“让我进吧”)
async function async1() {
console.log('async1')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeOut')
}, 0)
async1()
new Promise((resolve) => {
console.log('promise')
resolve('promise2')
}).then((data) => {
console.log(data)
return ('promise3')
}).then((data) => {
console.log(data);
}).then((data) => {
console.log(data);
})
console.log('script end')
答案:
"script start"
"async1"
"async2"
"promise"
"script end"
"async1 end"
"promise2"
"promise3"
undefined
undefined
"setTimeOut"
分析:
- 首先打印第一个遇到的主线程任务“script start”
- 调用 async1(),打印 await 前的“async1”,然后调用 async2(),打印“async2”
- 调用 Promise 构造函数,这里是和主线程一起的同步任务,打印“promise”,然后是最后一个主线任务,打印“script end”
- 开始执行异步任务,先执行微任务 await 后的,打印“async1 end”
- 继续执行微任务,原生 Promise 相关的(这里是then),打印“promise2”
- then() 接受的参数是在它前面的 Promise resolve 出来的东西,三个 then() 打印 data,只有第一个的前面有 resolve 出东西,所以打印一个“promise2”加两个 undefined
- 微任务执行完毕,最后执行宏任务 setTimeout,打印“setTimeOut”