news 2026/5/8 3:51:46

Fiber调度原理解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fiber调度原理解析

Fiber 调度原理(Scheduler)学习笔记

一、requestIdleCallback 原理

  1. 核心作用
  • requestIdleCallback 是浏览器提供的空闲期调度 API,其核心能力是在浏览器主线程空闲的时间段内执行回调任务,不会阻塞页面的关键渲染流程(布局、绘制、用户交互等)。
  • 浏览器每帧(约 16.67ms,60fps)的执行流程为:处理用户事件 → 执行 JS → 布局(Layout) → 绘制(Paint),若某一帧完成所有核心工作后仍有剩余时间,该时间段即为「空闲期」,requestIdleCallback 注册的任务会在此期间执行。
  1. 关键特性
  • 非高优先级:空闲期可被新的高优先级任务(如用户点击、滚动)抢占,未执行完的任务会在下一个空闲期继续
  • 超时兜底:支持传入第二个参数 { timeout: 毫秒数 },若任务在超时时间内仍未被执行,浏览器会在主线程繁忙时强制执行,避免任务永久挂起;
  • 空闲时间可控:回调函数会接收一个 IdleDeadline 参数,通过 deadline.timeRemaining() 可获取当前空闲期剩余时间,用于判断任务是否需要中断。
  1. 局限性(React 弃用原生 API 的原因)
  • 兼容性差:IE 完全不支持,部分移动端浏览器支持度低;
  • 触发频率低:浏览器在页面闲置时才会频繁触发,页面繁忙时可能几秒才触发一次,无法满足 React 高频调度需求;
  • 精度不足:时间计算存在偏差,无法精准控制任务执行的时间切片。

总结:requestIdleCallback 是 React Scheduler 的设计灵感来源,但 React 基于其核心思想实现了自研的 Scheduler,解决了原生 API 的所有问题。

二、Scheduler 核心 —— 优先级设计

React Scheduler 的核心目标是按优先级调度任务,避免低优先级任务阻塞高优先级任务(如用户输入、点击比数据请求渲染优先级更高),其优先级体系是基于时间的过期策略,核心分为「优先级定义」和「调度规则」两部分。

  1. 优先级核心定义:过期时间(expirationTime)
    Scheduler 不使用「字符串 / 数字等级」定义优先级(如 high/low、1-5),而是通过任务的过期时间(expirationTime) 表征优先级:
  • 过期时间越短(任务越快过期),优先级越高;
  • 过期时间越长(任务越晚过期),优先级越低;
  • 已过期的任务(当前时间 ≥ 过期时间)会被立即执行,抢占所有未过期任务的执行权。
  1. 内置优先级等级(从高到低)
    React 为常用场景预设了优先级,对应不同的过期时间(单位:ms),核心等级如下(实际源码中为常量定义):
优先级等级过期时间适用场景
同步优先级0紧急更新(如用户输入、点击)
高优先级250动画、过渡效果
中优先级5000普通 UI 更新(如列表渲染)
低优先级10000非紧急任务(如数据预加载)
空闲优先级Infinity完全空闲时执行(如日志上报)
  1. 核心调度规则
  • 任务队列按过期时间升序排序(高优先级任务排在队首);
  • 每次仅执行队首的高优先级任务,低优先级任务需等待高优先级任务执行完毕;
  • 执行过程中若有新的更高优先级任务进入队列,立即中断当前任务,先执行新任务(「抢占式调度」核心);
  • 所有任务均未过期时,按「时间切片」执行,避免阻塞主线程。

三、时间切片(Time Slicing)实现

时间切片是 Scheduler 最核心的实现,将长任务拆分为多个可中断的小任务,每个小任务执行时间不超过一个「切片时间」,剩余任务放到下一个切片执行,从而保证主线程不被长期阻塞,页面保持流畅。

  1. 时间切片的核心目标
    突破 JS 单线程限制(无法真正并行),通过「分块执行 + 可中断」模拟并行效果,确保:
  • 每个切片执行时间 ≤ 5ms(React 预设,远小于浏览器一帧 16.67ms);
  • 浏览器有足够时间处理一帧的核心工作(布局、绘制、用户交互);
  • 任务执行过程可被高优先级任务抢占,无卡顿。
  1. 核心实现原理(替代原生 requestIdleCallback)
    React 自研了基于 requestAnimationFrame + MessageChannel 的空闲期检测机制,解决原生 API 缺陷,核心流程如下:
  • 通过 requestAnimationFrame 获取每帧的开始时间,计算出当前帧的剩余可用时间(16.67ms - 已执行时间);
  • 使用 MessageChannel 创建微任务级别的调度通道,将任务放到 MessageChannel 的回调中执行(优先级高于宏任务,避免任务延迟);
  • 执行任务前做「剩余时间检测」:通过 performance.now() 计算当前切片已执行时间,若超过预设切片时间(5ms),立即中断任务;
  • 中断后将剩余任务重新加入任务队列,等待下一个切片时间继续执行;
  • 若执行过程中检测到高优先级任务或浏览器无空闲时间,立即中断,优先处理主线程核心工作。
  1. 核心实现要点
  • 可中断:任务执行过程中无全局锁,通过「剩余时间检测」主动中断,而非被动等待;
  • 无阻塞:每个切片执行时间极短,浏览器有足够时间处理一帧的所有核心工作;
  • 抢占式:中断后高优先级任务可插队,保证用户交互等紧急操作的响应速度;
  • 兼容性强:基于浏览器通用 API(requestAnimationFrame、MessageChannel),无兼容性短板。

四、模拟实现:时间切片(Time Slicing)

  1. 实现目标
  • 模拟 React Scheduler 核心的时间切片能力,实现:
  • 长任务自动拆分为小任务,每个小任务执行时间 ≤ 5ms;
  • 任务执行过程可被中断,剩余任务自动续跑;
  • 不阻塞主线程,页面可正常响应用户交互。
  1. 代码实现
/** * 模拟 React Scheduler 时间切片实现 * 核心:分块执行长任务 + 剩余时间检测 + 可中断 */classTimeSlicingScheduler{constructor(){this.taskQueue=[];// 任务队列this.isRunning=false;// 是否正在执行任务,避免重复调度this.timeSlice=5;// 切片时间,默认5ms(同React)}/** * 添加任务到队列 * @param {Function} task - 要执行的任务(需是可分块的迭代器函数) * @param {number} priority - 优先级(数字越小,优先级越高) */addTask(task,priority=10){this.taskQueue.push({task:this.wrapTask(task),// 包装为迭代器,支持分块执行priority,startTime:performance.now(),});// 按优先级升序排序(高优先级在前)this.taskQueue.sort((a,b)=>a.priority-b.priority);// 启动调度this.schedule();}/** * 将普通函数包装为迭代器,支持分块执行(核心:可中断) * @param {Function} task - 原始长任务 * @returns {Generator} 迭代器对象 */wrapTask(task){returnfunction*(){yieldtask();// 分块执行,支持中断后续跑}();}/** * 核心调度方法:时间切片执行任务 */schedule(){// 若已有任务在执行,直接返回(避免重复执行)if(this.isRunning)return;this.isRunning=true;// 启动任务执行:使用 requestAnimationFrame 对齐浏览器帧constframeCallback=(timestamp)=>{// 执行任务,直到切片时间用尽或任务队列为空consthasMoreTasks=this.executeTasks(timestamp);if(hasMoreTasks){// 还有剩余任务,继续调度下一帧requestAnimationFrame(frameCallback);}else{// 任务执行完毕,重置状态this.isRunning=false;}};requestAnimationFrame(frameCallback);}/** * 执行任务核心逻辑:剩余时间检测 + 分块执行 * @param {number} startTime - 当前帧开始时间 * @returns {boolean} 是否还有剩余任务 */executeTasks(startTime){letcurrentTask=this.taskQueue[0];if(!currentTask)returnfalse;const{task}=currentTask;letshouldContinue=true;// 循环执行,直到切片时间用尽或任务执行完毕while(shouldContinue&&currentTask){// 检测剩余时间:当前时间 - 帧开始时间 > 切片时间 → 中断constelapsedTime=performance.now()-startTime;if(elapsedTime>this.timeSlice){shouldContinue=false;// 切片时间用尽,中断break;}// 执行当前任务的一个小切片(迭代器next)constresult=task.next();// 若任务执行完毕(迭代器done),从队列中移除if(result.done){this.taskQueue.shift();}// 更新当前任务(队列首元素)currentTask=this.taskQueue[0];}// 返回是否还有剩余任务(队列非空 或 当前任务未执行完)returnthis.taskQueue.length>0||(currentTask&&!shouldContinue);}}// ---------------------- 测试用例 ----------------------// 1. 初始化调度器constscheduler=newTimeSlicingScheduler();// 2. 模拟一个长任务:循环10000次,打印计数(正常执行会阻塞主线程)functionlongTask(){letcount=0;return()=>{// 每次切片执行100次,分100块执行(避免单次阻塞)for(leti=0;i<100;i++){count++;if(count%1000===0){console.log(`长任务执行中:${count}/10000`);}}// 任务未执行完时,继续返回执行if(count<10000){returnfalse;}console.log("长任务执行完毕!");returntrue;};}// 3. 添加长任务到调度器(优先级10)scheduler.addTask(longTask(),10);// 4. 添加高优先级任务(优先级1,会插队执行)scheduler.addTask(()=>{console.log("【高优先级任务】执行:用户点击事件处理");returntrue;},1);// 测试:页面点击事件(验证不阻塞)document.addEventListener("click",()=>{console.log("页面点击响应:无卡顿,主线程未被阻塞!");});
  1. 代码核心说明
    1. 迭代器包装(wrapTask):将长任务包装为 Generator 迭代器,通过 task.next() 实现分块执行,这是任务可中断的核心(中断后下次执行从上次的 next() 继续);
    2. 优先级排序:添加任务时按优先级升序排序,保证高优先级任务始终在队首执行;
    3. 帧对齐(requestAnimationFrame):让任务执行与浏览器帧同步,避免浪费空闲时间;
    4. 剩余时间检测:通过 performance.now() 计算已执行时间,超过 5ms 立即中断,保证主线程空闲;
    5. 无阻塞验证:测试用例中添加了页面点击事件,执行长任务时点击页面可正常响应,无卡顿。
  2. 运行效果
  • 高优先级任务先执行:打印「【高优先级任务】执行:用户点击事件处理」;
  • 长任务分块执行:每次打印「长任务执行中:1000/10000」「2000/10000」…,每块执行时间 ≤5ms;
  • 页面交互正常:执行过程中点击页面,立即打印「页面点击响应:无卡顿,主线程未被阻塞!」;
  • 任务执行完毕:最后打印「长任务执行完毕!」,无任何阻塞。

五、Scheduler 与 Fiber 架构的关联

  • Scheduler 是 React Fiber 架构的调度层核心,为 Fiber 树的构建和更新提供底层支持:
  • Fiber 树的调和(Reconciliation)过程被拆分为多个小任务,由 Scheduler 按时间切片执行;
  • Fiber 节点的优先级与 Scheduler 任务优先级一致,保证高优先级的 Fiber 更新(如用户交互)可抢占低优先级更新;
  • Scheduler 的抢占式调度是 Fiber 「可中断调和」的基础,调和过程中可随时中断,跳过高优先级任务的调和;
  • 调和过程中若时间切片用尽或有高优先级任务,Scheduler 会中断执行,将剩余工作交给下一个切片,保证页面流畅。

六、核心知识点总结

  1. requestIdleCallback 是 Scheduler 的设计灵感,但其兼容性和触发频率问题导致 React 自研调度机制;
  2. Scheduler 优先级基于过期时间,过期时间越短优先级越高,支持抢占式调度;
  3. 时间切片的核心是长任务分块 + 可中断 + 剩余时间检测,通过 requestAnimationFrame + MessageChannel 实现;
  4. 时间切片的关键是主动中断,而非被动等待,保证主线程不被阻塞;
  5. Scheduler 是 Fiber 架构的底层支撑,为 Fiber 调和提供优先级调度和时间切片能力。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 20:40:28

快速排序 vs 归并排序:从原理到性能的终极对比(含Benchmark测试)

快速排序 vs 归并排序&#xff1a;从原理到性能的终极对比&#xff08;含Benchmark测试&#xff09; 在算法优化的世界里&#xff0c;排序算法的选择往往决定了程序性能的上限。当我们需要处理海量数据时&#xff0c;一个高效的排序算法可以节省数小时甚至数天的计算时间。快速…

作者头像 李华
网站建设 2026/4/17 21:21:27

HarmonyOS在语文教学中的应用-8. 古诗配乐朗读《静夜思》

8. 古诗配乐朗读(对应:「8」 静夜思) 功能介绍: 针对《静夜思》开发的古诗鉴赏应用。界面采用水墨风格,背景有一轮明月缓缓移动。学生点击诗句,会有标准的古筝配乐和朗诵播放。同时提供“注释”按钮,点击后解释“疑是地上霜”等诗句的含义,营造宁静的意境,帮助学生背…

作者头像 李华
网站建设 2026/4/17 13:31:32

【限时解密】SITS2026官方未公布的隐藏维度:框架对Ollama本地模型热切换支持度、多租户Agent隔离强度、以及国产信创环境适配成熟度(麒麟V10/统信UOS实测排名)

第一章&#xff1a;SITS2026发布&#xff1a;AIAgent开发框架对比 2026奇点智能技术大会(https://ml-summit.org) 核心框架概览 SITS2026正式发布了三款主流AI Agent开发框架的基准评估结果&#xff1a;LangChain v0.3、LlamaIndex v0.11与Semantic Kernel v1.0.7。本次评估覆…

作者头像 李华
网站建设 2026/4/18 0:48:17

Verilog 进阶学习指南:从入门到精通的必备书单(附资源)

1. Verilog学习路径规划&#xff1a;从菜鸟到高手的三个阶段 第一次接触Verilog时&#xff0c;我被那些看似天书般的模块声明和always块搞得晕头转向。后来在导师的指导下&#xff0c;才发现学习Verilog需要分阶段突破&#xff0c;就像打游戏升级一样要循序渐进。根据我十年带新…

作者头像 李华
网站建设 2026/4/17 20:52:39

phpast扩展怎么用_抽象语法树操作指南【操作】

phpast扩展不支持PHP 8.0&#xff0c;仅兼容至PHP 7.2&#xff1b;推荐改用nikic/php-parser库&#xff0c;它纯PHP实现、支持PHP 5.6–8.3全版本&#xff0c;提供AST遍历、修改与代码生成功能。phpast 扩展不支持 PHP 8.0&#xff0c;别白费时间编译phpast 是一个已停止维护的…

作者头像 李华