最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一眼就懂的 React 调度算法

    正文概述 掘金(灿儿哈擦苏)   2021-01-06   436

     react 的调度,采用 优先级调度(Priority),代码量大且复杂,看了下 fre 中的调度实现(最短剩余时间优先),比较精简且适合快速学习。

    问题产生:GUI渲染线程与JS引擎是互斥的,所以需要避免 js 长时间占用导致页面绘制卡顿。

    调度核心:频繁发起一个宏任务,根据事件循环机制避免 js 长时间占用(这里需要 fiber 的架构模式)。

    代码实现:

    const queue = []
    // react中为 5ms,fre中为16ms 是多少目前看无所谓 
    const threshold = 1000 / 60
    const unit = []
    let deadline  = 0
    
    // 收集 flushWork 并触发一个宏任务
    export const schedule = (cb) => unit.push(cb) === 1 && postMessage()
    
    // 对外暴露的入口,进行任务收集
    export const scheduleWork = (callback, time) => {
      const job = {
        callback,
        time,
      }
      queue.push(job)
      schedule(flushWork)
    }
    
    // 不兼容 MessageChannel 则使用 setTimeout
    const postMessage = (() => {
      const cb = () => unit.splice(0, unit.length).forEach((c) => c())
      if (typeof MessageChannel !== 'undefined') {
        const { port1, port2 } = new MessageChannel()
        port1.onmessage = cb
        return () => port2.postMessage(null)
      }
      return () => setTimeout(cb)
    })()
    
    // 这里执行传入的任务
    const flush = (initTime) => {
      let currentTime = initTime
      let job = peek(queue)
      while (job) {
        const timeout = job.time + 3000 <= currentTime
        // 超过了 16 ms 立即终止 交还控制权给浏览器一下
        if (!timeout && shouldYield()) break
        const callback = job.callback
        job.callback = null
        // 这里的 next 存在则意味着fiber的中断  下段代码进行相关解释
        const next = callback(timeout)
        if (next) {
          job.callback = next
        } else {
          queue.shift()
        }
        job = peek(queue)
        currentTime = getTime()
      }
      return !!job
    }
    
    // 还有任务一直递归执行
    const flushWork = () => {
      const currentTime = getTime()
      deadline = currentTime + threshold
      flush(currentTime) && schedule(flushWork)
    }
    
    // 是否过期
    export const shouldYield = () => {
      return getTime() >= deadline
    }
    
    export const getTime = () => performance.now()
    
    // 最短剩余时间优先执行(react根据优先级进行的过期时间排序)
    const peek = (queue) => {
      queue.sort((a, b) => a.time - b.time)
      return queue[0]
    }
    

    这是调度的所有逻辑,短小精悍,核心逻辑和 react 中几乎一致。

    上面有个问题是 next 的获取,next如何还存在则继续执行next,证明 cpu 拥挤,组件没有渲染完成。如果 next 没有了证明这个任务渲染完成要出队,然后再去取最小时间的任务继续执行。这里代码展示解答一下:

    function workLoopConcurrent() {
      // 这里会进行fiber的分片,那么中断后如何再继续执行呢?
      while (workInProgress !== null && !shouldYield()) {
        performUnitOfWork(workInProgress);
      }
    }
    

    调度入口(react 中的实现):

    function ensureRootIsScheduled(){
        ...
        newCallbackNode = scheduleCallback(
          schedulerPriorityLevel,
          performConcurrentWorkOnRoot.bind(null, root),
        );
        ...
    }
    function performConcurrentWorkOnRoot(root, didTimeout){
      ...
      if (root.callbackNode === originalCallbackNode) {
        // 这里的返回值就是调度那里的 next
        // 这样被中断的 fiber 就可以再继续执行workLoopConcurrent 进入循环和时间分片判断
        return performConcurrentWorkOnRoot.bind(null, root);
      }
      ...
    }
    

    看下 fre 的精简实现:

    export const dispatchUpdate = (fiber?: IFiber) => {
      ...
        scheduleWork(reconcileWork.bind(null, fiber), fiber.time)
      ...
    }
    
    const reconcileWork = (WIP, timeout: boolean): boolean => {
      while (WIP && (!shouldYield() || timeout)) WIP = reconcile(WIP)
      // 返回自身
      if (WIP && !timeout) return reconcileWork.bind(null, WIP)
      if (preCommit) commitWork(preCommit)
      return null
    }
    

    来张图辅助理解:

    一眼就懂的 React 调度算法

    总之,调度解决的问题就是要避免 js 长时间占用导致页面绘制卡顿,其他问题暂不分析。

    更多源码分析请查看

    www.gitsu.cn

    github.com/yisar/fre​


    起源地下载网 » 一眼就懂的 React 调度算法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元