事件循环有两种类型的任务:宏任务(macrotasks)和微任务(microtasks)。
宏任务常见的有:脚本、setTimeout、setInterval等,
微任务常见的有:Promise.then、MutationObserver等。
事件循环的算法大致如下:
- 1、从宏任务队列中取出并执行最旧的任务(例如“脚本”)。
- 2、执行所有微任务:当微任务队列不为空时,取出并执行最旧的微任务。
- 3、如果有任何变化,则渲染页面。
- 4、重复以上步骤直到宏任务队列为空。
提前小知识:
- setTimeout()函数1用于在指定的毫秒数后执行一个回调函数,但不会阻塞后续代码的执行。
- Promise对象2用于表示一个异步操作的最终状态(完成或失败),以及该操作的结果值。
- then()方法2用于在Promise对象状态变为fulfilled时,执行一个回调函数,并返回一个新的Promise对象。
了解这些基本知识后看这道题就简单多了:
setTimeout(function(){
console.log(1);
},100);
new Promise(function(resolve){
console.log(2);
resolve();
console.log(3);
}).then(function(){
console.log(4);
new Promise((resolve,reject)=>{
console.log(5);
setTimeout(()=>{
console.log(6);
},10);
})
})
console.log(7);
console.log(8);
可以先自己结合上诉知识推理一下,输出结果是什么?
、、
、、
、、
、、
、、
输出结果:2 3 7 8 4 5 6 1
怎么样 做对了吗?
下面我进行一个保姆级的详细分析:
1、首先创建一个setTimeout()函数,指定在100ms后输出1,但不会立即执行,而是将其放入任务队列中等待调度。
2、创建一个Promise对象,并立即执行其构造函数中的同步代码,即输出2,此时将状态修改为fulfilled,并输出3。
3、此时调用then方法,在Promise状态改为fulfilled时,执行一个回调函数,这个回调函数同样不会立即执行,而是将其放入微任务队列中等待调度。
4、然后继续执行同步代码,即输出7和8。
5、此时同步代码已经执行完,开始检查微任务队列(微任务队列优先级高于任务队列)是否有待执行的任务,发现有一个then方法,则执行它,输出4,并创建一个新的Promise对象,则立即执行其构造函数中的同步代码,输出5,同样将其状态改为fulfilled。
6、此时微任务里面还有一个宏任务setTimeout,因此再创建一个setTimeout()函数,10ms后输出6,并将其压入宏任务队列。
7、现在所有微任务都执行完了,宏任务队列里面有两个setTimeout函数,一个100ms,一个10ms,按照顺序执行,先执行100ms的setTimeout函数,再执行10ms的setTimeout函数,因此先输出时间短的输出6,再输出1。
(这里按照时间长短决定输出结果,如果两个函数都是100ms,则按顺序执行,先输出1,再输出6)