JS是单线程语言,因此同一时刻只能执行一行代码,因此,在遇到异步任务时,就出现了事件循环机制来处理异步任务。
任务队列:同步任务队列和异步任务队列
异步任务队列:(根据任务源分为)宏任务和微任务
宏任务:浏览器或者Node环境实现的
宏任务队列:setTimeout,setInterval,I/O事件(鼠标点击事件、console.log()等),setImmediate(一个EventLoop执行完执行),requestAnimationFrame(页面重绘前的操作),页面重绘
微任务:js引擎自身提供的
微任务队列:Promise,MutationObserver(浏览器,DOM发生变化时立即放入微任务队列(如属性变化),多次修改也只触发一次),process.nextTick(NodeJS)
setTimeout和setImmediate:
//setTimeout在设置延迟后执行,setImmediate在一个Event Loop结束时调用
setTimeout(_ => console.log('setTimeout'))
setImmediate(_ => console.log('setImmediate'))
//在主线程直接执行这两个操作,输出顺序是不一定的
为了保证setTimeout调用在setImmediate之前,将一个事件循环过程加长,保证setImmediate的晚执行,比如添加一个while循环
MutationObserver:监听DOM变化,在最后一个DOM变化后调用,DOM多次改变,只调用一次
function domChange(){
document.body.removeChild()
document.body.appendChild()
document.body.appendChild()
}
new MutationObserver(()=>{
console.log("MutationObserver")
})
//domChange调用时输出1次MutationObserver
requestAnimationFrame:页面重绘前调用
1、new MutationObserver(function () {
console.log('mutate');
}).observe(outer, {
attributes: true,
});//注册微任务
2、outer.setAttribute('data-random', Math.random());//执行调用1
3、requestAnimationFrame(()=>{
console.log("requestAnimationFrame")
})//注册宏任务
//mutate
//requestAnimationFrame
处理过程:所有的任务可以分为同步任务和异步任务,所有同步任务在主线栈中执行,代码按顺序执行,同步代码立即执行,遇到异步代码,注册到宏任务队列或微任务队列,
同步代码执行完毕后检查微任务队列,执行完微任务队列,检查执行宏任务队列。
当触发事件时存在冒泡现象时:
通过addListener添加的click(手动点击DOM触发):同步代码-》微任务-》(冒泡)同步代码-》微任务-》两次宏任务
<div class="outer">
<div class="inner" style="border: 2px red solid">div</div>
</div>
<script>
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
new MutationObserver(function () {
console.log('mutate');
}).observe(outer, {
attributes: true,
});//注册微任务
function onClick() {
console.log('click');
setTimeout(function () {
console.log('timeout');
},0);
Promise.resolve().then(function () {
console.log('promise');
});
outer.setAttribute('data-random', Math.random());
requestAnimationFrame(()=>{
console.log("requestAnimationFrame")
})
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
</script>
//执行结果
click
promise
mutate
click
promise
mutate
requestAnimationFrame//两次click执行的宏任务
requestAnimationFrame
timeout//两次click执行的宏任务
timeout
点击内层div调用onClick方法,执行同步代码,微任务,进入冒泡再次调用onClick,执行同步代码,微任务,两次宏任务
这里有两个问题不太明白:
(1)MutationObserver什么时候压入微任务队列的
(2)requestAnimationFrame和timeout为什么是连续输出而不是交叉输出
页面加载时直接调用(node)inner.click():同步代码-》(冒泡)同步代码-》微任务-》宏任务,应该不是一个I/O事件
inner.click()相当于一个同步代码执行,因此在microTask之前执行
<script>
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
new MutationObserver(function () {
console.log('mutate');
}).observe(outer, {
attributes: true,
});//注册微任务
function onClick() {
console.log('click');
setTimeout(function () {
console.log('timeout');
},0);
Promise.resolve().then(function () {
console.log('promise');
});
outer.setAttribute('data-random', Math.random());
requestAnimationFrame(()=>{
console.log("requestAnimationFrame")
})
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click();//第一次页面加载时直接调用click方法,非手动触发,此时输出结果
click
click
promise
mutate
promise
requestAnimationFrame
requestAnimationFrame
timeout
timeout
</script>
这里有个问题不太明白:
为什么mutate只输出了一次
new Promise异步操作的输出顺序
new Promise(resolve => {
resolve();//A
}).then(() => {
new Promise(resolve => {
resolve();//B1
})
.then(()=>{new Promise((resolve) => {
resolve()
})
.then(() => {new Promise((resolve)=>{
resolve()
})
.then(()=>{
console.log(1)
})
}
)
.then(() => { console.log(11) })
})
.then(() => { console.log(222); });
})
.then(() => {
new Promise((resolve => {
resolve()
}))
.then(() => {
new Promise((resolve) => {
resolve()
})
.then(() => { console.log(333) })
.then(() => { console.log(444) })
})
.then(() => {
console.log(555);
})
})
.then(() => {
console.log(666);
})
//输出结果:
222
666
1
11
333
555
444
结果与操作系的计算速度等有关
参考自:[https://stackoverflow.com/questions/58270410/how-to-understand-this-promise-execution-order](https://stackoverflow.com/questions/58270410/how-to-understand-this-promise-execution-order)
所有参考自:https://www.cnblogs.com/yugege/p/9598265.html
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/#level-1-bossfight
https://juejin.im/post/6844903657264136200