简单实现一下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任务。 通过时间切片的方法,确定每一个宏任务可以执行多长时间。

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

相关推荐
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ18 小时前
React 怎么区分导入的是组件还是函数,或者是对象
前端·react.js·前端框架
前端无涯18 小时前
React/Vue 代理配置全攻略:Vite 与 Webpack 实战指南
vue.js·react.js
San301 天前
深度解析 React 组件化开发:从 Props 通信到样式管理的进阶指南
前端·javascript·react.js
AAA阿giao1 天前
深度解析 React 项目架构:从文件结构到核心 API 的全面拆解
前端·javascript·react.js
昨晚我输给了一辆AE861 天前
关于 react-hook-form 的 isValid 在有些场景下的值总是 false 问题
前端·react.js
Mintopia1 天前
🏗️ B端架构中的用户归因与埋点最佳实践
前端·react.js·架构
Blossom.1181 天前
大模型AI Agent实战:ReAct框架从零实现与金融研报分析系统
人工智能·学习·react.js·stable diffusion·金融·aigc·知识图谱
南山安1 天前
React 学习:父传子的单项数据流——props
javascript·react.js·前端框架
soda_yo1 天前
React哲学:保持组件纯粹 哈气就要哈得纯粹
前端·react.js·设计