简单实现一下react的任务中断和恢复,和事件分片机制

首先需要知道的是在不同的环境实现任务的终止和恢复的方法不一样

  • 在node环境是通过setimmedate实现
  • 在浏览器环境,如果支持MessageChannel则是通过messageChannel实现,如果不支持是通过setTimeOut实现 这里我们主要讲通过MessageChannel实现方法

先讲一下messageChannel的简单用法

js 复制代码
// 对于MessageChannel用法可以去MDN文档查询
let ch = new MessageChannel();
let {port1,port2} = ch;
// port1执行下面的方法时,会触发port2的事件。并且这个事件是个宏任务,这很重要
port1.postMessage('hello');
// 这个任务是一个宏任务
port2.onmessage = (e=>{
    console.log(e)
    )

浏览器的任务循环机制需要了解一下,这里主要讲宏任务,就不再说微任务了。

同步任务--->页面刷新(如果需要)--->第一个宏任务--->页面刷新(如果需要)---->第二个宏任务---->页面刷新(如果需要)----省略

javascript 复制代码
// 用来记录每次启动一个宏任务的时间
let startTime = 0;
// 保存一个宏任务,执行截止时间,endTime = startTime+during
let endTime = 0;
// 一个宏任务可以执行多长时间
const during = 5;
let {port1, port2} = new MessageChannel();
let count = 0;
// 需要不停执行的任务,我们可以把他理解成diff过程需要执行很多的diff任务
function work() {
    count++
}
// 当前时间大于任务的截止时间,就需要停止任务
function shouldStop() {
// 为什么采用window.performance.now(),而不是Date.now();
//  `Date.now()` 返回的是当前时间戳(以毫秒为单位),精度为毫秒级。并且于系统的当前时间有关
//  `window.performance.now()` 返回的是相对于页面加载开始时刻的高精度时间戳(以毫秒为单位),通常精度为微秒级。
    return window.performance.now() > endTime;
}
// 用来启动一个宏任务,
function startWork() {
    // 需要跟新启动宏任务时,任务的启动时间
    startTime = window.performance.now();
    // 计算出本次宏任务的截止时间
    endTime = startTime + during;
    // 发起消息,触发port2绑定的事件开始执行
    port1.postMessage(null)
}

// 执行任务
function workConcurrentWork() {
    // 每次执行完一个工作的时候,都需要判断是否需要截止执行
    while (!shouldStop()) {
        // work表示一个又一个需要执行的任务,他应该从任务队列中取出,这里为了简单,没有使用任务队列
        work() 
    }
    // 这里我写的是一个死逻辑,正常是判断当前的任务队列中是否还有需要执行的任务
    if (count < 100000000) {
        // 开启一个新的宏任务
        startWork()
    }
}

port2.onmessage = workConcurrentWork;
startWork();

let a = document.getElementById('a');
let b = document.getElementById('b');
a.addEventListener('click', () => {
    for (let i = 0; i < 100; i++) {
        b.append(String(count))
    }
})

let a = document.getElementById('a');
let b = document.getElementById('b');
a.addEventListener('click', () => {
    let p = document.createElement('p')
    p.innerText = String(count)
    b.appendChild(p)
})

对应的html代码贴上

css 复制代码
<body>
<button id="a">add item</button>
<div id="b"></div>
</body>

我们在页面上不停的点击按钮,可以明显发现元素会直接添加进去,页面也不没有发生卡顿,而且我们的count一直在增加。 我们可以看下火焰图

可以看到task任务是一段一段的,表示一个又一个宏任务。而在宏任务的中间,可以执行其他任务,并刷新页面。

总结

我们可以总结出任务机制。就是通过fiber将一个原有的递归调用,变成一个个以fiber节点为执行单元的小任务。通过messageChannel将原本需要同步执行的任务,变成一个又一个宏任务,并且在宏任务中去多次执行fiber任务。 通过时间切片的方法,确定每一个宏任务可以执行多长时间。

至此讲完,如有不足,请指点。

相关推荐
binbin_521 小时前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
张元清2 小时前
React useObjectUrl Hook:预览文件与 Blob,不留内存泄漏(2026)
javascript·react.js
真的想不出名儿6 小时前
Md编辑器整合-react
前端·react.js·编辑器
牛仔只喝牛奶21 小时前
多端跨端技术选型:小程序 + H5 + App 的两栈拆分落地
react native·react.js
wear工程师1 天前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
wanghowie1 天前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
csj501 天前
前端基础之《React(11)—state声明式》
前端·react.js
张元清1 天前
React useLocalStorage Hook:SSR 安全的持久化状态(2026)
javascript·react.js
析数塔1 天前
2026前端框架深度解析:React Compiler、Angular Zoneless、Vue 3.5重写游戏规则
前端·vue.js·react.js
杖雍皓1 天前
没有服务器如何构建现代 Web 应用:平台选型与组合策略
运维·服务器·前端·react.js·github·netlify·vercel