先看一道题
这个就涉及到JavaScript事件轮询中的宏任务和微任务。那么,你能说清楚到底宏任务和微任务是什么?是谁发起的?为什么微任务的执行要先于宏任务呢?
如上图所示,其中MessageQueue也叫taskQueue,MicrotaskQueue也叫jobsQueue.
定时器,xhr,各种dom事件(点击。。)等异步放到消息队列,promise等放到微任务里面。宏任务与微任务我们放到后面说
现在我们来看开头那道题的执行过程
可以看到执行先后顺序为 调用栈 > 微任务 > 消息队列。
每次执行消息队列中的任务前都会检查微任务队列吗?
如果消息队列中有task1和task2,当执行完task1过程中,微任务队列加入了一个task3,那么下一步是执行task2还是执行task3呢?如果先执行task3,那么说明再执行每个消息队列中的任务前都会先检查其它任务队列,先执行优先级高的任务队列中的任务。
var promise_task = new Promise((resolve,reject) => {
resolve('task3');
});
var resolve_callback = (resolve_message) => {console.log(resolve_message)}
var message_task1 = () => {
promise_task.then(resolve_callback);
console.log('task1');
}
var message_task2 = () => {console.log('task2');}
setTimeout(message_task1,0);
setTimeout(message_task2,0);
//result:
//task1
//task3
//task2
事实证明js在每次执行消息队列中的任务前都会检查其它任务队列(至少会检查微任务队列),根据队列优先级决定先执行哪个队列中的任务。
调用栈呢?
那么每次在检查消息队列前会不会检查调用栈?
这个问题我觉得会,不仅消息队列前会,微任务队列前也会,因为这两个队列都是将代码放入调用栈中执行,所有无论什么时候在调用栈中加入代码(stop),下一步无论原本该执行消息队列还是微任务队列都会变成执行(stop)。
我们声明一个promise和一个message_task函数。在这个promise的回调函数中使用setTimeout创建一个message_task的消息队列任务,同时在message_task中调用promise.then 函数创建一个微任务队列任务。这样两个任务会循环创建并循环执行,之后在执行过程中我们在控制台执行alert(‘stop’),看是是否会立即出现弹窗
var promise_task = new Promise((resolve,reject) => {
resolve('j_task');
});
var resolve_callback = (resolve_message) => {
setTimeout(message_task,0);
console.log(resolve_message);
}
var message_task = () => {
promise_task.then(resolve_callback);
console.log('m_task');
}
promise_task.then(resolve_callback);
//result:
//console会循环打印 j_task 和 m_task
//这时在console中键入alert('stop')命令,弹出alert框,console中打印暂停
即每次选择执行任务前(或者每次任务结束后),js会根据主任务队列,job queue,message queue的优先级来挑选将要执行下一个任务是哪个。
宏任务与微任务
那么到底什么是宏任务和微任务呢,
每次宏任务结束后都会去执行当前微任务队列,然后再去执行下一个宏任务。
- 每个script标签是一个宏任务。
<script>
console.log(22)
Promise.resolve().then(() => {
console.log(11)
})
console.log(44)
</script>
<script>
console.log(33)
</script>
result:
// 22 44 11 33 共执行两个宏任务,即两个script标签
- 每个定时器是一个宏任务
<script>
setTimeout(() =>{
Promise.resolve().then(
console.log(1111),
Promise.resolve().then(console.log(5555))
)
console.log(333)
setTimeout(() =>{
Promise.resolve().then(console.log(2222))
console.log(444)
},0)
},0)
</script>
result: //1 5 3 2 4 两个宏任务,即两个定时器
当然还有就不一一列举了
//script1
<script>
var promise_task = new Promise((resolve,reject) => {
resolve('j_task');
});
var resolve_callback = (resolve_message) => {
setTimeout(message_task,0);
console.log(resolve_message);
}
var message_task = () => {
promise_task.then(resolve_callback);
console.log('m_task');
}
promise_task.then(resolve_callback);
</script>
//script2
<script>
console.log(333)
setTimeout(() => {
console.log(444)
}, 0);
// alert('stop')
console.log(555)
</script>
result:
j_task
333
555
m_task
j_task
444
m_task
j_task
m_task
j_task
......
下图分析一下上面结果,截至到下图步骤6,共执行了 script1 script2 两个宏任务。
结合之前调用栈,消息队列,微任务队列三者的优先级,以及宏任务定义
补充
查资料的过程中发现有一个tick定义,但是我看不懂到底什么才算是一次tick,关于tick每个人说的都不太一样,这个等以后搞懂再说吧。(tick我现在的理解是执行一个宏任务就是一个tick,在tick结束后,下一个tick开始前渲染页面)