浏览器环境的 Event loop

252 阅读3分钟

浏览器环境的 Event loop

相关概念

event loop

PS:Node 环境的 event loop 与浏览器有差异,这里只讨论浏览器环境

根据 HTML 规范定义,event loop 是浏览器为了协调事件、用户交互、脚本、渲染、网络必须使用的处理模型

event loop 可以根据不同环境分类为:

  • window event loop: 浏览器窗口环境的事件循环
  • worker event loop: web worker 和 service worker 环境的事件循环
  • worklet event loop: worklet 环境的事件循环

task

PS: 关于宏任务(macrotask),html 规范中并无相关描述,个人理解其应该是社区为了区分微任务创造的概念

task,任务。任务是一个结构体(struct),封装了负责以下操作的算法

  • Events:事件
  • Parsing: 解析文档
  • Callbacks: 回调
  • Using a resource: 获取资源
  • Reacting to DOM manipulation:响应 DOM 操作

一个 event loop 维护一到多个 任务队列(task queue),每个任务来特定的 任务源(task source),每个任务源关联一个特定的任务队列

常见任务源有这些:

  • DOM manipulation: DOM 操作
  • user interaction: 用户交互
  • networking: 网络活动
  • navigation and traversal:网页历史前进后退

使用任务源分类,是为了方便决定任务的优先级,比如给用户交互相关的任务队列 3/4 的优先权,就可以让界面保持响应性,但又不卡死其它任务队列

任务和微任务上属于 JS 引擎层面的概念,JS 代码执行必然属于任务或微任务,但反之不一定,比如绘制 UI 便是渲染引擎的工作

与 JS 相关的任务主要有:

  • script 代码执行
  • 各类事件监听函数回调
  • setTimeout,setInterval 定时器回调
  • requestAnimationFrame,requestIdleCallback 回调
  • postMessage,MessageChannel 通信

microtask

PS: html 规范中的 microtask 可以与 ECMA 规范中的 Jobs 概念对应

microtask,微任务。一个 event loop 只维护一个微任务队列(microtask queue),通过算法分类进入微任务队列的任务,即为微任务

PS: 微任务是任务,但微任务队列不是任务队列

微任务主要有:

  • Promise
  • MutationObserver

event loop 处理模型

html 规范中给出了非常详细的模型:event-loop-processing-model

简单提炼一下涉及到 JS 代码执行的部分:

  1. 选择一个任务队列
  2. 从任务队列中取出一个任务执行
  3. 检查微任务队列是否为空,如果为空,进入步骤 5
  4. 依次执行微任务队列中的微任务
  5. 检测是否需要渲染 UI,如需要渲染 UI,进入步骤 6
  6. 在渲染 UI 之前执行 requestAnimationFrame 回调
  7. 检测任务队列是否为空,如果不为空则回到步骤 2
  8. 执行 requestIdleCallback 回调

需要注意的点:

  • 从步骤 2 到 7 算一次 event loop,当前任务队列清空后才会执行 requestIdleCallBack 回调
  • 微任务队列只有一个,而微任务对象清空后才能进入下一次 event loop,如果在微任务中不断添加新的微任务,便会阻塞 event loop,无法进入下一步的 UI 渲染,导致卡顿
  • 渲染 UI 的基本条件应该是: 当前 event loop 所消耗的时长大于一个动画帧(animation frame)时长。这个时长由用户设备的屏幕刷新率相关,60HZ 刷新率为 1/60 s,即 16.67ms

参考资料

HTML Standard (whatwg.org)