浏览器事件循环(Event Loop)

273 阅读3分钟

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. There are two kinds of event loops: those for browsing contexts, and those for workers.

翻译:

为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,用户引擎必须使用 event loops

Event Loop 包含两类:一类是基于 Browsing Context ,一种是基于 Worker ,二者是独立运行的。

  • 事件:PostMessage、MutationObserver 等
  • 用户交互:onclick、onscroll、onresize 等
  • 渲染:解析 DOM、CSS 等
  • 脚本:js 脚本执行
  • I/O
  • requestAnimationFrame
  • UI 渲染

任务队列

javascript 是单线程的,所以每个任务的需要执行都需要等待上一个任务执行完毕才可以执行下一个任务。这就需要排队形成一个队列(任务队列)

任务分两种:同步任务(synchronous)和异步任务(asynchronous)

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
  2. 在主线程外,存在一个“任务队列”(task queue)。只要异步任务有了运行结果,就在“任务队列”之中放置一个事件
  3. 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,查看有哪些事件。那些对应的异步任务,马上就结束等待状态,进入执行栈,开始执行
  4. 在主线程不断重复以上 1、2、3 步骤。

同步任务和异步任务的角度理解.png

宏任务和微任务

除了广义的同步任务和异步任务,Javascript 单线程中的任务可以细分为宏任务(macrotask)和微任务(microtask)

  • 宏任务:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering、new Promise是同步立即执行的
  • 微任务:process.nextTick、Object.observe、MutationObserver、Promise(Promise.then...)
  1. 宏任务进入主线程,执行过程中会收集微任务加入到微任务队列。
  2. 宏任务执行完毕后,立马执行微任务队列中的任务。微任务执行过程中将再次收集宏任务,并加入到宏任务队列中。
  3. 不断重复执行 1、2 两个步骤.

宏任务和微任务的角度.png

注意:宏任务需要多次事件循环才能执行完,微任务是一次性执行完的;

  • 宏任务macrotask:

(事件队列中的每一个事件都是一个macrotask)

优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval

比如:setImmediate指定的回调函数,总是排在setTimeout前面

  • 微任务包括: 优先级:process.nextTick > Promise > MutationObserver

总结

事件环.jpg

  1. 明确 event loop 的机制,js 主线程是单线程的,当第一个主线程执行过程中,遇到宏任务就放到宏任务队列,遇到微任务就放到微任务队列,该次主线程执行完了之后,会把微任务队列先清空,紧接着 GUI渲染进程渲染页面,然后再去宏任务队列里面把已经到时的宏任务提取出来放到主线程中执行,这样不断的循环这个过程。

  2. 明确,页面渲染是在清空微任务之后。