JavaScript知识 - event loop
JavaScript资料整理
Event loop
JS中的event loop
众所周知
JS是门非阻塞单线程语言,因为在最初JS就是为了和浏览器交互而诞生的。如果JS是门多线程的语言话,我们在多个线程中处理DOM就可能会发生问题(一个线程中新加节点,另一个线程中删除节点)
JS在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到Task(有多种task) 队列中。一旦执行栈为空,EventLoop就会从Task队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说JS中的异步还是同步行为
console.log('script start'); |
不同的任务源会被分配到不同的
Task队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在ES6规范中,microtask称为 jobs,macrotask 称为 task
console.log('script start'); |
以上代码虽然
setTimeout写在Promise之前,但是因为Promise属于微任务而setTimeout属于宏任务
微任务
process.nextTickpromiseObject.observeMutationObserver
宏任务
scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering
宏任务中包括了
script,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务
所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染 UI
- 然后开始下一轮
Event loop,执行宏任务中的异步代码
通过上述的
Event loop顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作DOM的话,为了更快的响应界面响应,我们可以把操作DOM放入微任务中
Node 中的 Event loop
Node中的Event loop和浏览器中的不相同。Node的Event loop分为6个阶段,它们会按照顺序反复运行
┌───────────────────────┐ |
timer
timers阶段会执行setTimeout和setInterval- 一个 timer 指定的时间并不是准确时间,而是在达到这个时间后尽快执行回调,可能会因为系统正在执行别的事务而延迟
I/O
I/O阶段会执行除了close事件,定时器和setImmediate的回调
poll
poll阶段很重要,这一阶段中,系统会做两件事情- 执行到点的定时器
- 执行
poll队列中的事件
- 并且当
poll中没有定时器的情况下,会发现以下两件事情- 如果
poll队列不为空,会遍历回调队列并同步执行,直到队列为空或者系统限制 - 如果
poll队列为空,会有两件事发生 - 如果有
setImmediate需要执行,poll阶段会停止并且进入到check阶段执行setImmediate - 如果没有
setImmediate需要执行,会等待回调被加入到队列中并立即执行回调 - 如果有别的定时器需要被执行,会回到
timer阶段执行回调。
- 如果
check
check阶段执行setImmediate
close callbacks
close callbacks阶段执行close事件- 并且在
Node中,有些情况下的定时器执行顺序是随机的
setTimeout(() => { |
上面介绍的都是
macrotask的执行情况,microtask会在以上每个阶段完成后立即执行
setTimeout(()=>{ |
Node中的process.nextTick会先于其他microtask执行
setTimeout(() => { |
