美文共赏

scheduler模块

2021-12-12  本文已影响0人  jluemmmm

scheduler模块用于管理重绘完成后回调的执行逻辑。从输出分析,对整个调度过程进行梳理。

基础前提

浏览器渲染与事件循环

浏览器采用多进程架构,包含浏览器主进程、渲染进程、插件进程、GPU进程。每开启一个tab时浏览器就会开启一个渲染进程,该进程里包含多个线程:负责运行jsdomcss计算和页面渲染的主线程,运行worker的工作线程等。主线程解析html时,遇到script标签,会暂停html的解析,并开始加载、解析并执行js代码、为了调度事件、用户交互、渲染、网络请求这些操作,主线程会通过事件循环来处理。事件循环的过程为:

常见的宏任务:事件回调、xhr回调、定时器、I/OMessageChannel

常见的微任务:PromiseGeneratorAsync/AwaitMutationObserver

时间片

js在浏览器中的执行是单线程的,长时间的js任务执行可能会阻塞其他浏览器任务,如页面渲染、用户交互等,有可能会造成用户的卡顿感。schedule中采用时间分片的策略,将任务细化为不同的优先级,利用浏览器的空闲时间进行任务的执行保证UI操作的流畅。浏览器的调度API主要分为两种,高优先级的requestAnimationFrame与低优先级的requestIdleCallback

js任务分解到时间片中执行后,一次时间循环最多只执行一个时间片,若还有未完成的任务,将这些任务放到后面的事件循环的时间片中执行,保证不会阻塞其他的浏览器任务。

requestAnimationFrame

requestAnimationFrame传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。回调函数执行次数通常是每秒60次,在大多数遵循w3c建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。大多数浏览器中,当 requestAnimationFrame运行在后台标签页或隐藏的iframe中时,requestAnimationFrame会被暂停调用。

requestAnimationFrame函数接收一个接收DOMHighResTimeStamp参数的callback函数作为参数,返回一个requestIdcancelAnimationFrame以取消。

requestIdleCallback

浏览器每秒一般60帧,帧与帧的间隔成为时间片,长度为 1000 / 6016ms,如果一帧渲染完成的时间小于16ms,这个时间片就有空闲时间。空闲时间会被执行requestIdleCallback的回调函数。当超过timeout时间还不执行callback时,callback将会被强制执行,造成的后果是,阻塞本地渲染,延长渲染时间,造成卡顿、延迟等。

callback函数接收IdleDeadline接口类型的参数,是一个对象,包含两个属性

requestIndleCallback会返回一个id,传入cancelIdleCallback可结束对应的回调。

使用时间片的实现:

scheduler每执行一次performWorkUntilDeadline函数表示执行了一个时间片,执行该函数前会通过MessageChannelsetTimeout将该函数放入宏任务队列,优先使用MessageChannel

每执行一个时间片时,将时间片长度 yieldInterval和过期时间deadline放入执行的任务中,并通返回值判断是否存在未执行完的任务(表示时间片已用完)。若存在为执行完的任务,则让任务在下次事件循环继续执行。

export {
 ImmediatePriority as unstable_ImmediatePriority, // 1
 UserBlockingPriority as unstable_UserBlockingPriority, // 2
 NormalPriority as unstable_NormalPriority, // 3
 IdlePriority as unstable_IdlePriority, // 5
 LowPriority as unstable_LowPriority, // 4
 unstable_runWithPriority,
 unstable_next,
 unstable_scheduleCallback,
 unstable_cancelCallback,
 unstable_wrapCallback,
 unstable_getCurrentPriorityLevel,
 shouldYieldToHost as unstable_shouldYield,
 unstable_requestPaint,
 unstable_continueExecution,
 unstable_pauseExecution,
 unstable_getFirstCallbackNode,
 getCurrentTime as unstable_now,
 forceFrameRate as unstable_forceFrameRate,
};
export const unstable_Profiling = enableProfiling
 ? {
     startLoggingProfilingEvents,
     stopLoggingProfilingEvents,
   }
 : null;

输出函数分析

任务优先级

react内对任务优先级的定义。Scheduler中任务有不同的优先级,每个优先级有对应的过期时间,在生成任务时根据优先级和创建时间生成任务的过期时间,任务过期后才会放入taskQueue执行,否则放入timerQueue等待执行。

优先级 含义 过期时间 过期时间的值
NoPriority 0 无优先级
ImmediatePriority 1 最高优先级 IMMEDIATE_PRIORITY_TIMEOUT -1
UserBlockingPriority 2 用户阻塞型优先级 USER_BLOCKING_PRIORITY_TIMEOUT 250
NormalPriority 3 普通优先级 NORMAL_PRIORITY_TIMEOUT 5000
LowPriority 4 低优先级 LOW_PRIORITY_TIMEOUT 10000
IdlePriority 5 空闲优先级 IDLE_PRIORITY_TIMEOUT maxSigned31BitInt = Math.pow(2, 30) - 1

环境中设置变量分析


//任务存储在小顶堆上
var taskQueue = [] // 任务队列
var timerQueue = []; // 延时任务队列
var taskIdCounter = 1; // 递增id计数器, 用于维护插入顺序
var isSchedulerPaused = false; // 暂停调度程序,用于调试
var currentTask = null; // 当前任务
var currentPriorityLevel = NormalPriority; //3 当前执行任务的优先级
var isPerformingWork = false; // 是否正在执行任务,在执行工作时设置的, 以防止重新进入
var isHostCallbackScheduled = false; // 是否有主任务正在执行,是否调度了 taskQueue, isHostCallbackScheduled为true后才把时间片放到宏任务队列,之后开始执行任务
var isHostTimeoutScheduled = false; // 是否有延时任务正在执行,是否调度了 timerQueue, 设置了 timeout回调

SchedulerHostConfig

let requestHostCallback; // 请求回调
let cancelHostCallback; // 取消回调
let requestHostTimeout; // 请求超时
let cancelHostTimeout; // 取消超时
let shouldYieldToHost;
let requestPaint; // 请求绘制
let getCurrentTime; // 获取当前时间, 优先用 performance.now(), 或者用 Date.now() - 初始时间
let forceFrameRate; // 强制帧率

unstable_runWithPriority

unstable_runWithPriority(*priorityLevel*, *eventHandler*)主要逻辑:

unstable_next

unstable_next(*eventHandler*)主要逻辑:

unstable_scheduleCallback

整体流程:

Untitled Diagram.drawio.png

unstable_scheduleCallback(*priorityLevel*, *callback*, *options*)主要逻辑为,根据输入返回newTask

var newTask = {
  id: taskIdCounter++,
  callback,
  priorityLevel,
  startTime,
  expirationTime,
  sortIndex: -1,
};

https://someu.github.io/2020-11-10/react-scheduler%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/

https://juejin.cn/post/6889314677528985614

https://juejin.cn/post/6914089940649246734

md格式的文件直接粘过来有点丑,待补充

上一篇 下一篇

猜你喜欢

热点阅读