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

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

相关推荐
Highcharts.js2 天前
Highcharts Orbit 完全指南:把每一张图表变成交互式分析工具
react.js·前端框架·highcharts·orbit·使用指南·ai 图表·交互式分析工具
鸿蒙开发2 天前
小程序存档写到一半怎么办?双槽保存与损坏恢复实战
前端·javascript·react.js
古茗前端团队4 天前
由 key 引发的思考
前端·javascript·react.js
用户298698530147 天前
React 前端将 Excel 工作表导出为图片的技术实践
javascript·react.js·excel
終不似少年遊*8 天前
【安全业务Agent】从场景到编排
python·react.js·ai·langchain·agent·安全架构·工作流
JavaPub-rodert9 天前
从零搭建通用管理后台:ShiyuAdmin 的 Go、React 与 RBAC 实践
开发语言·react.js·golang
Highcharts.js9 天前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
JudithHuang9 天前
React 嵌套路由与布局
前端·react.js·arcgis·react·react router
前端小D9 天前
React Native项目开发(iOS侧为例)
javascript·react native·react.js
志尊宝10 天前
AI原生前端开发(042):AI辅助的路由设计与优化
javascript·react.js·ai-native