javascript事件循环机制、宏任务和微任务(原理解析)

一、Event Loop
Event Loop即事件循环,是指 浏览器Node (宿主) 的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

二、同步任务和异步任务
Javascript单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在异步任务有了结果后,将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。 上述过程会不断重复,也就是常说的Event Loop(事件循环)

注意:队列(Queue)遵循先进先出原则

三、宏任务:macrotask
宏任务是由宿主( 浏览器windowNode)发起的。
前端开发常见宏任务:包括整体代码scriptsetTimeoutsetIntervalsetImmediate

四、微任务:microtask
微任务由JavaScript自身发起。
前端开发常见微任务:PromiseProcess.nextTick(Node独有)

下面用一个经典题目做具体分析:

console.log('1');

// 定义注解 setTimeout_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')
    })
})
process.nextTick(function() {
    
    
    console.log('6');
})
new Promise(function(resolve) {
    
    
    console.log('7');
    resolve();
}).then(function() {
    
    
    console.log('8')
})

// setTimeout_2
setTimeout(function() {
    
    
    console.log('9');
    process.nextTick(function() {
    
    
        console.log('10');
    })
    new Promise(function(resolve) {
    
    
        console.log('11');
        resolve();
    }).then(function() {
    
    
        console.log('12')
    })
})

// 输出结果:  1 7 6 8 2 4 3 5 9 11 10 12

1、第一轮事件循环流程分析如下:

(1)整体script作为第一个宏任务进入主线程,同步 console.log('1') 执行,输出1
(2)执行 setTimeout_1 异步 ,将其回调函数分发到宏任务Event Queue(队列)中
(3)执行 process.nextTick 异步 ,将其回调函数分发到微任务Event Queue
(4)执行 new Promise ,Promise 新建后立即执行,所以 console.log('7') 执行,输出 7.then 是异步,将其分发到微任务Event Queue
(5)执行 setTimeout_2 异步 ,将其分发到宏任务Event Queue(队列)中

第一轮事件循环宏任务结束时各Event Queue的情况:
同步代码输出结果为: 1、7
宏任务Event QueuesetTimeout_1setTimeout_2
微任务Event Queue整体script 中的 process.nextTicknew Promise().then()

主线程内的任务执行完毕为空,会去微任务Event Queue读取对应的函数,进入主线程执行:
此时执行process.nextTicknew Promise().then() 输出结果为:6、8

第一轮完毕输出结果: 1、7、6、8

重点:微任务Event Queue执行完毕为空,会去宏任务Event Queue读取对应的函数,到此第一轮事件循环彻底结束,第二轮事件循环开始

2、第二轮事件循环流程分析如下: (执行宏任务Event Queue中的setTimeout_1

(1)setTimeout_1进入主线程,同步 console.log('2') 执行,输出2
(2)执行 process.nextTick 异步 ,将其回调函数分发到微任务Event Queue
(3)执行 new Promise ,Promise 新建后立即执行,所以 console.log('4') 执行,输出 4.then 是异步,将其分发到微任务Event Queue

第二轮事件循环宏任务结束时各Event Queue的情况:
同步代码输出结果为: 2、4
宏任务Event QueuesetTimeout_2
微任务Event QueuesetTimeout_1 中的 process.nextTicknew Promise().then()

第二轮主线程内的任务执行完毕为空,会去微任务Event Queue读取对应的函数

第二轮完毕输出结果: 2、4、3、5

第二轮事件循环彻底结束,第三轮setTimeout_2事件循环开始

3、第三轮事件循环流程分析如下: (执行宏任务Event Queue中的setTimeout_2

(1)setTimeout_2进入主线程,同步 console.log('9') 执行,输出9
(2)执行 process.nextTick 异步 ,将其回调函数分发到微任务Event Queue
(3)执行 new Promise ,Promise 新建后立即执行,所以 console.log('11') 执行,输出 11.then 是异步,将其分发到微任务Event Queue

第三轮主线程内的任务执行完毕为空,会去微任务Event Queue读取对应的函数

第三轮完毕输出结果: 9、11、10、12

第三轮执行完毕,至此任务队列皆为空,整段代码,共进行了三次事件循环

完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。

总结:整体script作为第一个宏任务进入主线程,进行第一次事件循环,若遇到宏任务,则将其回调函数分发到宏任务队列中、若遇到微任务,则将其回调函数分发到微任务队列中。同步代码执行完毕,微任务队列进入主线程直至任务执行完毕为空,下一个宏任务进入主线程意味着开始进行第二次事件循环。上述过程会不断重复,直到宏任务队列执行完毕为空,也就是常说的Event Loop(事件循环)机制了。

猜你喜欢

转载自blog.csdn.net/qq_44094296/article/details/125888078