JS Event Loop
# 一.Javascript的执行机制
JavaScript 是单线程。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。
如果前一个任务耗时很长,后一个任务就不得不一直等着。
为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。
JS执行过程中,主线程从"任务队列"中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环。
Event Loop是javascript的执行机制
# 二.宏任务微任务
"任务队列"中的任务分为两种
宏任务:正常的异步任务都是宏任务,最常见的就是定时器setInterval、setTimeout、IO任务
微任务:微任务出现比较晚,process.nextTick(callback)、Promise和async属于微任务(当然,async就是promise)
此机制具体如下:
主线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查microtask队列是否为空(执行完一个任务的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去任务队列中取下一个任务执行。
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件**,** 然后再去宏任务队列中取出一个事件。
同一次事件循环中微任务永远在宏任务之前执行
所以其实微任务就是得到优先执行的异步任务
按照官方的设想,任务之间是不平等的,有些任务对用户体验影响大,就应该优先执行,而有些任务属于背景任务(比如定时器),晚点执行没有什么问题,所以设计了这种优先级队列的方式
# 三、nodejs的Event-loop
#
node 和 浏览器 eventLoop的最主要的区别在于浏览器中的微任务是在每个相应的宏任务中执行的,而nodejs中的微任务是在不同阶段之间执行的。
# 4.代码
了解原理后看例子:
setTimeout(()=>{
console.log('setTimeout1')
},0)
let p = new Promise((resolve,reject)=>{
console.log('Promise1')
resolve()
})
p.then(()=>{
console.log('Promise2')
})
复制代码
2
3
4
5
6
7
8
9
10
11
容易得出输出结果是Promise1,Promise2,setTimeout1
# 四、JS为什么要区分微任务和宏任务?
个人认为,是为了给任务分级, 分级的目的是为了对不同级别的任务区分对待。
代码都是对现实世界的抽象,机场也有vip通道,任务分优先级是很正常的,要不然紧急任务怎么插队。
一个Event Loop,Microtask 是在 Macrotask 之后调用,Microtask 会在下一个Event Loop 之前执行调用完,并且其中会将 Microtask 执行当中新注册的 Microtask 一并调用执行完,然后才开始下一次 Event loop,所以如果有新的 Macrotask 就需要一直等待,等到上一个 Event loop 当中 Microtask 被清空为止。
由此可见, 我们可以在下一次 Event loop 之前进行插队。如果不区分 Microtask 和 Macrotask,那就无法在下一次 Event loop 之前进行插队,其中新注册的任务得等到下一个 Macrotask 完成之后才能进行,这中间可能你需要的状态就无法在下一个 Macrotask 中得到同步。状态的同步对于视图来说至关重要,这也就牵扯到了为什么 javascript 是单线程的原因所在。