React Scheduling
React Scheduling 是 React v16 后 Reconciler (Fiber Reconciler) 的一部分, 指的是 React 何时确定何时应执行 work 的过程。
问题背景
这个问题看 Dan 在 JSConf 2018 的 Part 1 就能够理解.
由于浏览器中 JS 的单线程环境,需要面对以下问题:
长时间运行的任务会导致帧丢失。 所以需要确保所有任务都较小,并且可以在几毫秒内完成,以便可以在一帧内运行它们。
不同的任务具有不同的优先级。 优先考虑用户输入总体上会带来更好的体验。所以需要一种定义顺序并相应安排任务的方法。
React 采用了两种模式来解决上诉问题:
Concurrent React(时间切片)。 在渲染期间暂停和增量更新。类似与 Git Commit。
Scheduler。 在浏览器中注册具有不同优先级的回调。
Concurrent React还在 Experimental 阶段Concurrent Features 在 React 18 已经正式发布
Scheduler
Scheduler。 通用协作主线程 Scheduler 是由 React Core 团队开发的,可以在浏览器中注册具有不同优先级的回调。
Scheduler 目前支持的优先级:
https://github.com/facebook/react/blob/4c6470cb3b821f3664955290cd4c4c7ac0de733a/packages/scheduler/src/SchedulerPriorities.js
React 中的采用的 Scheduler 的优先级:
https://github.com/facebook/react/blob/4c6470cb3b821f3664955290cd4c4c7ac0de733a/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js#L57-L63
Immediate用于需要同步运行的任务。UserBlocking(250 毫秒超时)用于应因用户交互(例如,单击按钮)而运行的任务。Normal(5s 超时),让您不必感到瞬间更新。Low(10 秒超时)用于可以推迟但最终仍必须完成的任务(例如,分析通知)。Idle(无超时)用于根本不需要运行的任务(例如,隐藏的屏幕外内容)。
制定超时时间以避免饥饿(starvation)问题。 即使有很多高优先级的工作要做,低优先级的工作也可以连续运行。
Scheduler 会将所有已注册的回调存储在按到期时间(注册回调的时间加上优先级超时)的顺序排列的列表中。 然后,Scheduler 将自己注册一个回调,该回调在浏览器绘制下一帧之后运行。 在此回调中,Scheduler 将执行尽可能多的已注册回调,直到需要渲染下一帧为止。
当前实现中,Scheduler 通过在 requestAnimationFrame() 回调中使用 postMessage()实现的。
Scheduler 的使用场景
仅仅在需要优化的时候才需要 Scheduler。 如果当前 APP 运行流畅,完全没有必要。
使用 Scheduler
import {
unstable_next,
unstable_LowPriority,
unstable_scheduleCallback,
} from "scheduler";
// 高消耗的 API,对其降级处理
function sendDeferredAnalyticsNotification(value) {
unstable_scheduleCallback(unstable_LowPriority, function () {
sendAnalyticsNotification(value);
});
}
function SearchBox(props) {
const [inputValue, setInputValue] = React.useState();
function handleChange(event) {
const value = event.target.value;
setInputValue(value);
unstable_next(function () {
props.onChange(value);
sendAnalyticsNotification(value);
});
}
return <input type="text" value={inputValue} onChange={handleChange} />;
}Scheduler 的局限
使用 Scheduler,可以控制执行回调的顺序,并且可以在 Concurrent 模式下直接使用。
目前仍有两个限制:
资源斗争。 Scheduler 尝试使用所有可用资源。 如果 Scheduler 的多个实例在同一线程上运行并争夺资源,则会导致问题。我们需要确保应用程序的所有部分都将使用相同的 Scheduler 实例。
使用户定义的任务与浏览器工作保持平衡。 由于 Scheduler 在浏览器中运行,因此它只能访问浏览器公开的 API。 无法影响比如 Html 的渲染、回收周期。
Scheduling API in the Browser 就是为了解决该问题
参考
最后更新于