说起js的事件循环机制,那真是我心中的痛。

我一开始理解js的事件循环时候,我理解错了,我把setTimeout理解成了微任务, 从而只要是关于js事件循环,我都理解错了,一直到一个阳光明媚的下午, 我才真正的找到了正确的理解方式。

# 任务队列

每个事件循环都至少有一个宏任务队列(Task Queue)和一个微任务队列(Microtask Queue), 分别压入需要执行的宏任务和微任务。

宏任务: script内的同步代码、setTimeout、setInterval、I/O、rendering、requestAnimationFrame
微任务: process.nextTick、Promise callback、MutationObserver

微任务也是有优先级的

process.nextTick() > Promise.then()
nextTick属于一个特殊API,他会立即执行,然后才会继续执行event loop

setTimeout-0和setImmediate

无法具体确定哪个先执行
但是如果同时处在异步I/O callback里面,setImmediate先执行

# 事件循环

1.主线程执行任务栈内的任务
2.执行任务完毕去检查任务队列,首先会处理掉所有微任务
3.更新render,每次事件循结束浏览器都会更新渲染
4.再去宏任务队列中取出一个事件压入任务栈,开始下一轮循环

事件循环就是重复上述过程

执行顺序

执行macrotask -> js stack空时执行microtask -> UI render -> 开始下一轮循环

# 实例分析

宏任务1 -> 微任务1,微任务2,宏任务2;
微任务1 -> 微任务3,宏任务3,微任务4;
微任务2 -> 宏任务4,微任务5;
宏任务2 -> 微任务6,宏任务5

开始分析:

  • 开始执行宏任务1
    已执行队列:宏1
    宏任务队列:宏2
    微任务队列:微1 微2

  • 执行微任务1
    已执行队列:宏1 微1
    宏任务队列:宏2 宏3
    微任务队列:微2 微3 微4

  • 执行微任务2
    已执行队列:宏1 微1 微2
    宏任务队列:宏2 宏3 宏4
    微任务队列:微3 微4 微5

  • 执行微任务3、4、5
    已执行队列:宏1 微1 微2 微3 微4 微5
    宏任务队列:宏2 宏3 宏4
    微任务队列:

  • 执行宏任务2
    已执行队列:宏1 微1 微2 微3 微4 微5 宏2
    宏任务队列:宏3 宏4 宏5
    微任务队列:微6

  • 执行微任务6
    已执行队列:宏1 微1 微2 微3 微4 微5 宏2 微6
    宏任务队列:宏3 宏4 宏5
    微任务队列:

  • 执行宏任务3、4、5
    已执行队列:宏1 微1 微2 微3 微4 微5 宏2 微6 宏3 宏4 宏5
    宏任务队列:
    微任务队列:

  • 结束

# 再看一个

console.log('1');      
setTimeout(function() {     
    console.log('2');       
    process.nextTick(function() {
        console.log('3');   
    })
    new Promise(function(resolve) {
        console.log('4');   
        resolve();
    }).then(function() {
        console.log('5')  
    })
    console.log('t')    
})
process.nextTick(function() {
    console.log('6');         
})
new Promise(function(resolve) {     
    console.log('7');          
    resolve();
}).then(function() {
    console.log('8')           
})
setTimeout(function() {
    console.log('9');          
    process.nextTick(function() {
        console.log('10');     
    })
    new Promise(function(resolve) {
        console.log('11');   
        resolve();
    }).then(function() {
        console.log('12')   
    })
})
  • 执行log1 log7
    已执行:1 7
    宏队列:s2345t s9_10_11_12
    微队列:p6 t8

  • 执行微任务p6 t8
    已执行:1 7 6 8
    宏队列:s2345t s9_10_11_12
    微队列:

  • 执行宏任务s2345t
    已执行:1 7 6 8 2 4 t
    宏队列:s9_10_11_12
    微队列:p3 t5

  • 执行微任务p3 t5
    已执行:1 7 6 8 2 4 t 3 5
    宏队列:s9_10_11_12
    微队列:

  • 执行宏任务s9_10_11_12
    已执行:1 7 6 8 2 4 t 3 5 9 11
    宏队列:
    微队列:p10 t12

  • 执行微任务p10 t12
    已执行:1 7 6 8 2 4 t 3 5 9 11 10 12
    宏队列:
    微队列:

  • 结束