由于js是单线程的,一次只能执行一个任务。那么多个任务堆在一起时,就要这些任务排排队。按照先进先出的规则,形成一条消息队列。
多个任务源,形成多条消息队列
这就形成一条事件循环的执行队列。
事件循环:就是一个执行消息队列的机制。
- 首先选择优先级最高的消息队列1中最先进入的任务,任务1(相当于宏任务),没有任务,则执行微任务
- 给事件循环做个标记,标记执行(说明这个事件循环机制已经存在执行任务了)
- 执行任务结束
- 这个事件循环就执行完毕了,再做个标记null(说明没有任务在执行)
- 把这个已经执行的任务1从队列里踢出
- 执行微任务:进入microtask检查点(performing a microtask checkpoint )。检查是否有微任务,如果有,来个小型的任务循环机制(如下, 执行微任务所示)。
- 渲染页面
- 回到第一步,选择下一个任务,任务2…任务3…任务4
简单地来说就是:
不断地从一条队列中去task任务,执行结束之后,查看是否有微任务,如果有就执行,没有就执行下一个task
执行微任务:
microtask检查点的标志为true——>
选择微任务1——>
标记执行——>
执行。完毕——>
做标记null——>
将微任务1踢队——>
循环下一个微任务——>
microtask检查点的标志为false
例一:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
分析:
-
console.log('script start');
同步函数,进入主线程就是任务1的函数执行栈中,先进先出排在第一 -
setTimeout(function() { console.log('setTimeout');}, 0);
回调函数,回调函数是宏任务,排在下一个任务,任务2。
-console.log('promise1');
和console.log('promise2');
都在promise中then中,属于异步函数。异步函数放在同步函数之后,属于任务1之后的微任务,分别为微任务1,微任务2 -
console.log('script end');
属于任务1,排在script start之后
例二:for循环和定时器
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000);
console.log(i)
}
定时器中,每当执行console.log(i)的时候,由于执行顺序优先级的问题,i早已变成了6,所以定时器中的每次打印都是6,隔一秒打印一次6
例三:for循环和带参定时器,let
带参的定时器回调,那么i每变化一次,带的参数都是不同的。