当前位置: 首页 > news >正文

React Fiber 风格任务调度库

React Fiber 风格任务调度库

下面是一个模仿 React Fiber 架构的任务调度库,它允许将大型任务分解为可增量执行的小单元,避免阻塞主线程。

安装

npm install fiber-scheduler
# 或
yarn add fiber-scheduler

核心代码

// fiber-scheduler.js// 任务优先级常量
const PriorityLevels = {IMMEDIATE: 0,    // 最高优先级,需要同步执行USER_BLOCKING: 1, // 用户交互相关NORMAL: 2,       // 普通优先级LOW: 3,          // 低优先级IDLE: 4,         // 空闲时执行
};// 默认配置
const defaultConfig = {frameTime: 16,      // 每帧最大执行时间(ms)timeoutTime: 100,   // 超时时间(ms)enableLogging: false, // 是否启用日志
};class FiberScheduler {constructor(config = {}) {this.config = { ...defaultConfig, ...config };this.taskQueue = [];this.currentTask = null;this.isPerformingWork = false;this.scheduledHostCallback = null;this.frameDeadline = 0;this.scheduled = false;// 绑定方法this.scheduleCallback = this.scheduleCallback.bind(this);this.unscheduleCallback = this.unscheduleCallback.bind(this);this.workLoop = this.workLoop.bind(this);this.requestHostCallback = this.requestHostCallback.bind(this);this.shouldYield = this.shouldYield.bind(this);// 初始化this._init();}_init() {// 设置消息通道用于调度const channel = new MessageChannel();this.port = channel.port2;channel.port1.onmessage = () => {if (this.scheduledHostCallback) {const currentTime = performance.now();const hasTimeRemaining = () => currentTime < this.frameDeadline;try {this.scheduledHostCallback(hasTimeRemaining, currentTime);} finally {this.scheduled = false;}}};}// 检查是否应该让出主线程shouldYield() {return performance.now() >= this.frameDeadline;}// 请求宿主回调requestHostCallback(callback) {this.scheduledHostCallback = callback;if (!this.scheduled) {this.scheduled = true;this.port.postMessage(null);}}// 工作循环workLoop(hasTimeRemaining, initialTime) {let currentTime = initialTime;// 设置当前帧的截止时间this.frameDeadline = currentTime + this.config.frameTime;// 处理当前任务if (this.currentTask === null) {// 从队列中获取优先级最高的任务this.currentTask = this.peek();}while (this.currentTask !== null) {if (this.shouldYield()) {// 时间片用完,暂停执行break;}const callback = this.currentTask.callback;if (typeof callback === 'function') {try {// 执行任务const continuationCallback = callback();currentTime = performance.now();if (typeof continuationCallback === 'function') {// 任务返回了延续函数,更新当前任务的回调this.currentTask.callback = continuationCallback;} else {// 任务已完成,从队列中移除this.pop();}} catch (error) {// 任务执行出错this.pop();throw error;}// 获取下一个任务this.currentTask = this.peek();} else {// 无效的回调,移除任务this.pop();this.currentTask = this.peek();}}// 如果还有任务,继续调度if (this.currentTask !== null) {this.requestHostCallback(this.workLoop);} else {this.isPerformingWork = false;}}// 获取最高优先级的任务peek() {return this.taskQueue[0] || null;}// 移除已完成的任务pop() {return this.taskQueue.shift();}// 将任务插入到队列中的正确位置(按优先级排序)push(task) {// 按优先级排序(数字越小优先级越高)for (let i = 0; i < this.taskQueue.length; i++) {if (task.priority < this.taskQueue[i].priority) {this.taskQueue.splice(i, 0, task);return;}}this.taskQueue.push(task);}// 调度回调函数scheduleCallback(priority, callback, options = {}) {const { timeout = this.config.timeoutTime } = options;const currentTime = performance.now();// 创建新任务const newTask = {callback,priority,startTime: currentTime,expirationTime: currentTime + timeout,id: Math.random().toString(36).substr(2, 9),};// 将任务添加到队列this.push(newTask);// 如果没有正在执行的工作,开始调度if (!this.isPerformingWork) {this.isPerformingWork = true;this.requestHostCallback(this.workLoop);}if (this.config.enableLogging) {console.log(`Scheduled task ${newTask.id} with priority ${priority}`);}// 返回取消函数return () => this.unscheduleCallback(newTask);}// 取消已调度的回调unscheduleCallback(task) {const index = this.taskQueue.findIndex(t => t.id === task.id);if (index !== -1) {this.taskQueue.splice(index, 1);if (this.config.enableLogging) {console.log(`Unscheduled task ${task.id}`);}}}// 批量调度多个任务scheduleBatch(priority, callbacks, options = {}) {const cancelFunctions = [];callbacks.forEach(callback => {const cancelFn = this.scheduleCallback(priority, callback, options);cancelFunctions.push(cancelFn);});// 返回批量取消函数return () => {cancelFunctions.forEach(cancelFn => cancelFn());};}
}// 导出库
module.exports = {FiberScheduler,PriorityLevels,
};

TypeScript 类型定义

// index.d.ts
declare module 'fiber-scheduler' {export type PriorityLevel = 0 | 1 | 2 | 3 | 4;export const PriorityLevels: {IMMEDIATE: PriorityLevel;USER_BLOCKING: PriorityLevel;NORMAL: PriorityLevel;LOW: PriorityLevel;IDLE: PriorityLevel;};export interface Task {id: string;callback: () => (void | (() => void));priority: PriorityLevel;startTime: number;expirationTime: number;}export interface SchedulerConfig {frameTime?: number;timeoutTime?: number;enableLogging?: boolean;}export interface ScheduleOptions {timeout?: number;}export class FiberScheduler {constructor(config?: SchedulerConfig);scheduleCallback(priority: PriorityLevel,callback: () => (void | (() => void)),options?: ScheduleOptions): () => void;scheduleBatch(priority: PriorityLevel,callbacks: Array<() => (void | (() => void))>,options?: ScheduleOptions): () => void;unscheduleCallback(task: Task): void;shouldYield(): boolean;}
}

使用示例

// 示例代码
const { FiberScheduler, PriorityLevels } = require('fiber-scheduler');// 创建调度器实例
const scheduler = new FiberScheduler({frameTime: 16, // 每帧16mstimeoutTime: 100, // 超时时间100msenableLogging: true, // 启用日志
});// 模拟一个耗时任务
function heavyTask(id, chunks = 10) {let currentChunk = 0;return function performChunk() {// 模拟处理数据块for (let i = 0; i < 1000000; i++) {// 模拟计算Math.sqrt(i) * Math.random();}currentChunk++;console.log(`Task ${id} completed chunk ${currentChunk}/${chunks}`);// 如果还有更多工作,返回延续函数if (currentChunk < chunks) {return performChunk;}// 任务完成console.log(`Task ${id} completed!`);};
}// 调度多个任务
const cancelTask1 = scheduler.scheduleCallback(PriorityLevels.NORMAL,heavyTask('A', 5)
);const cancelTask2 = scheduler.scheduleCallback(PriorityLevels.USER_BLOCKING,heavyTask('B', 3)
);// 批量调度任务
const cancelBatch = scheduler.scheduleBatch(PriorityLevels.LOW,[heavyTask('C', 2),heavyTask('D', 4),heavyTask('E', 3)]
);// 5秒后取消所有任务
setTimeout(() => {console.log('Cancelling all tasks...');cancelTask1();cancelTask2();cancelBatch();
}, 5000);

API 说明

FiberScheduler 类

构造函数
  • new FiberScheduler(config)
  • 参数:
    • config.frameTime: 每帧最大执行时间(ms),默认16ms
    • config.timeoutTime: 任务超时时间(ms),默认100ms
    • config.enableLogging: 是否启用日志,默认false
方法
  • scheduleCallback(priority, callback, options): 调度一个回调函数

    • priority: 优先级,使用PriorityLevels中的值
    • callback: 要执行的回调函数,可以返回一个延续函数
    • options.timeout: 任务超时时间(ms)
    • 返回: 取消该任务的函数
  • scheduleBatch(priority, callbacks, options): 批量调度多个回调函数

    • 参数同上,但callbacks是回调函数数组
    • 返回: 取消所有批量任务的函数
  • unscheduleCallback(task): 取消已调度的任务

  • shouldYield(): 检查是否应该让出主线程

PriorityLevels 常量

  • IMMEDIATE (0): 最高优先级,需要同步执行
  • USER_BLOCKING (1): 用户交互相关
  • NORMAL (2): 普通优先级
  • LOW (3): 低优先级
  • IDLE (4): 空闲时执行

使用场景

  1. 大型计算任务分解:将耗时计算分解为小块,避免阻塞UI
  2. 动画和交互:确保用户交互始终有最高优先级
  3. 数据批量处理:处理大量数据时保持应用响应性
  4. 后台任务:在空闲时执行低优先级任务

注意事项

  1. 任务函数应该能够被中断和恢复,返回延续函数是实现这一点的关键
  2. 合理设置优先级,确保关键任务能够及时执行
  3. 注意任务超时设置,避免长时间运行的任务影响用户体验
  4. 在不需要时及时取消任务,释放资源

这个库提供了类似React Fiber的任务调度能力,可以帮助开发者更好地管理JavaScript执行,保持应用的响应性。


文章转载自:

http://GHrBxqyV.psdsk.cn
http://VbGNAAUq.psdsk.cn
http://kzOPJBg0.psdsk.cn
http://rSfwAAAG.psdsk.cn
http://JMiS80LT.psdsk.cn
http://TpF9dSbk.psdsk.cn
http://MxTAswA1.psdsk.cn
http://gREX6Dw0.psdsk.cn
http://bWhIicgg.psdsk.cn
http://9glNDb7I.psdsk.cn
http://XESWsAWD.psdsk.cn
http://I411aktA.psdsk.cn
http://ux4XYd18.psdsk.cn
http://DMlYTlnw.psdsk.cn
http://TP7XjBpo.psdsk.cn
http://bkDAL0KJ.psdsk.cn
http://qcMdQj6B.psdsk.cn
http://Ax1SM0xd.psdsk.cn
http://xwLpCy32.psdsk.cn
http://d6Esqwrz.psdsk.cn
http://yymIj8EM.psdsk.cn
http://lz9CfZyA.psdsk.cn
http://9fZqHEzf.psdsk.cn
http://uD1gfAcY.psdsk.cn
http://bfM3JRNg.psdsk.cn
http://46jWkBn8.psdsk.cn
http://FVQlUi5d.psdsk.cn
http://sfTBLa2a.psdsk.cn
http://6CgPzROV.psdsk.cn
http://NwxeSg6p.psdsk.cn
http://www.dtcms.com/a/368908.html

相关文章:

  • Sentinel和Cluster,到底该怎么选?
  • 紧固卓越,智选固万基——五金及紧固件一站式采购新典范
  • android 四大组件—Activity源码详解
  • B树,B+树,B*树(无代码)
  • Redis到底什么,该怎么用
  • mysql中null值对in子查询的影响
  • 时间轮算法在workerman心跳检测中的实战应用
  • 不同行业视角下的数据分析
  • 探索Go语言接口的精妙世界
  • 如何在没有权限的服务器上下载NCCL
  • 常见Bash脚本漏洞分析与防御
  • 【算法笔记】异或运算
  • 数据结构:排序
  • mac清除浏览器缓存,超实用的3款热门浏览器清除缓存教程
  • 残差连接与归一化结合应用
  • 【知识点讲解】模型扩展法则(Scaling Law)与计算最优模型全面解析:从入门到前沿
  • MySQL锁篇-锁类型
  • LINUX_Ubunto学习《2》_shell指令学习、gitee
  • FastGPT源码解析 Agent知识库管理维护使用详解
  • MATLAB 2023a深度学习工具箱全面解析:从CNN、RNN、GAN到YOLO与U-Net,涵盖模型解释、迁移学习、时间序列预测与图像生成的完整实战指南
  • 均匀圆形阵抗干扰MATLAB仿真实录与特点解读
  • 《深入理解双向链表:增删改查及销毁操作》
  • 属性关键字
  • 【Linux基础】Linux系统管理:MBR分区实践详细操作指南
  • 国产化FPGA开发板:2050-基于JFMK50T4(XC7A50T)的核心板
  • 时隔4年麒麟重新登场!华为这8.8英寸新「手机」给我看麻了
  • 敏感词过滤这么玩?自定义注解 + DFA 算法,优雅又高效!
  • RPC内核细节(转载)
  • 如何将 Android 设备的系统底层日志(如内核日志、系统服务日志等)拷贝到 Windows 本地
  • Vue美化文字链接(超链接)