ECMAScript规范没有提到事件循环, 真正定义Event Loop执行顺序以及方法的细节是被归类在HTML Living Stardard里面的
HTML Living Stardard基本上就是规范了浏览器内核 如何实现的一套规则

事件循环的原则

Task: setTimeout
微任务: Promise MutationObserver
image.png
当一个任务开始执行后,不会被其他任何任务打断,除非浏览器决定终止执行该任务(如,单个任务执行时间过程,内存占用过大)

在一次迭代中, EventLoop首先检查宏任务队列,如果有任务在该队列中等待,则事件将依次执行,完成一个后
EventLoop将移步去处理微任务队列,直到队列中所有的微任务处理完毕并清空
EventLoop会检查是否需要更新UI渲染,如果是则会重新渲染UI视图

宏任务与微任务的区别: 单次循环中,最多处理一个宏任务(其余的在队列中等待),而队列中所有的微任务队列都会被处理

补充:
如果在微任务执行的时候,又触发新的微任务的话,这个新的微任务也是会被继续执行到当前

另外在规范上面有提到每一轮的EventLoop task结束后,不一定需要rendering
原因是为了要达到每秒60fps的效果(60 frams per second)
每次浏览器给出一个frame的时间是 1/60 ~= 16.67 ms
如果在16.7ms内连续进行两次dom操作的话,是有可能不会出现两次渲染的

Event Loop简化图

1600082749792.jpg
真正执行渲染时的thread跟执行js的thred是属于不同的thread
执行js的thread范畴是在task以及microtask中
但执行渲染时会通过另一个GUI thread去进行渲染

  1. function fn() {
  2. fn()
  3. }
  4. fn()
  5. function fn1() {
  6. setTimeout(fn1)
  7. }
  8. fn1()
  9. function fn2() {
  10. Promise.resolve().then(fn2)
  11. }
  12. fn2()

Vue.nextTick

异步更新队列

Macrotask: setTimeout setInterval setImmediate MessageChannel I/O UI rendering network
Microtask: process.nextTick, Promise, Object.observe(废弃), Mutation Observer

Node.js的Eventloop

image.png
Timers阶段: 这个阶段执行setTimeout和setInterval
I/O callbacks阶段: 这个阶段主要执行系统级别的回调函数,比如TCP连接失败的回调
idel、prepare阶段: 只是Node.js内部闲置、准备,可以忽略

参考链接

Event Loop运行机制解析
《JavaScript忍者秘籍 第13章》