浏览器环境的 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 代码执行的部分:
- 选择一个任务队列
- 从任务队列中取出一个任务执行
- 检查微任务队列是否为空,如果为空,进入步骤 5
- 依次执行微任务队列中的微任务
- 检测是否需要渲染 UI,如需要渲染 UI,进入步骤 6
- 在渲染 UI 之前执行 requestAnimationFrame 回调
- 检测任务队列是否为空,如果不为空则回到步骤 2
- 执行 requestIdleCallback 回调
需要注意的点:
- 从步骤 2 到 7 算一次 event loop,当前任务队列清空后才会执行 requestIdleCallBack 回调
- 微任务队列只有一个,而微任务对象清空后才能进入下一次 event loop,如果在微任务中不断添加新的微任务,便会阻塞 event loop,无法进入下一步的 UI 渲染,导致卡顿
- 渲染 UI 的基本条件应该是: 当前 event loop 所消耗的时长大于一个动画帧(animation frame)时长。这个时长由用户设备的屏幕刷新率相关,60HZ 刷新率为 1/60 s,即 16.67ms