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

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

相关推荐
zqx_713 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
TonyH20021 天前
webpack 4 的 30 个步骤构建 react 开发环境
前端·css·react.js·webpack·postcss·打包
掘金泥石流1 天前
React v19 的 React Complier 是如何优化 React 组件的,看 AI 是如何回答的
javascript·人工智能·react.js
lucifer3111 天前
深入解析 React 组件封装 —— 从业务需求到性能优化
前端·react.js
秃头女孩y2 天前
React基础-快速梳理
前端·react.js·前端框架
sophie旭2 天前
我要拿捏 react 系列二: React 架构设计
javascript·react.js·前端框架
BHDDGT2 天前
react-问卷星项目(5)
前端·javascript·react.js
liangshanbo12152 天前
将 Intersection Observer 与自定义 React Hook 结合使用
前端·react.js·前端框架
黄毛火烧雪下2 天前
React返回上一个页面,会重新挂载吗
前端·javascript·react.js