时间切片教程_切片软件手机版「建议收藏」

时间切片教程_切片软件手机版「建议收藏」最近在做思考总结,项目原来在做智能合同的项目关于文档比对导致页面卡断问题,项目组同事给了我一个切片编程的方案很棒,在此做个总结!需求源文档和修改后的文档,差异点存在近5000+的差异点需要在两个文档上进行颜色标注,整

最近在做思考总结,项目原来在做智能合同的项目关于文档比对导致页面卡断问题,项目组同事给了我一个切片编程的方案很棒,在此做个总结!

需求

源文档和修改后的文档,差异点存在近 5000+ 的差异点需要在两个文档上进行颜色标注,整个标注过程都是在进行 DOM 操作,做完后存在严重的页面卡死问题,甚至等待 5~8分钟才标注完成,甚至卡死!这个问题亟待解决优化!

分析

在事件循环机制中主线程需要全部执行完才会进行页面渲染,5000+ 的 DOM 操作严重阻塞了主线程,所以是不是可以给主线程让出空间,将dom操作任务放进宏任务队列,在下一次渲染之后继续执行DOM操作呢?

思路

1、执行主线程,DOM操作(先进行20次dom操作,剩余的任务放进setTimeout宏任务)
2、页面渲染,显示差异对比的颜色标注(上一次dom操作的结果)
3、执行宏任务中 20个 DOM 操作任务
4、页面渲染
5、重复 3、4

最后是结了这个方案,5000+ 的dom操作还是蛮顺滑的!最后我们讲讲时间切片思想

什么是时间切片

时间切片的核心思想是:如果任务不能在50毫秒内执行完,那么为了不阻塞主线程,这个任务应该让出主线程的控制权,使浏览器可以处理其他任务。让出控制权意味着停止执行当前任务,让浏览器去执行其他任务,随后再回来继续执行没有执行完的任务。

所以时间切片的目的是不阻塞主线程,而实现目的的技术手段是将一个长任务拆分成很多个不超过50ms的小任务分散在宏任务队列中执行。

在这里插入图片描述
上图可以看到主线程中有一个长任务,这个任务会阻塞主线程。使用时间切片将它切割成很多个小任务后,如下图所示。

在这里插入图片描述
可以看到现在的主线程有很多密密麻麻的小任务,我们将它放大后如下图所示。
在这里插入图片描述
可以看到每个小任务中间是有空隙的,代表着任务执行了一小段时间后,将让出主线程的控制权,让浏览器执行其他的任务。

使用时间切片的缺点是,任务运行的总时间变长了,这是因为它每处理完一个小任务后,主线程会空闲出来,并且在下一个小任务开始处理之前有一小段延迟。
但是为了避免卡死浏览器,这种取舍是很有必要的。

如何使用时间切片

目前发现有两种解决方案,不过起主体还是通过让出主线程的控制权
1、setTimeout
2、requestAnimationFrame
3、setTimeout + Generator

方案一:setTimeout

function request() { 
   
  let result = [];
  for (let i = 0; i < 100000; i++) { 
   
    result.push({ 
   
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => { 
   
  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  function loop(curTotal, curIndex) { 
   

    let pageCount = Math.min(curTotal , once);

    setTimeout(function () { 
   
      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) { 
   
        let li = document.createElement("li");
        li.innerText =
          "index: " + res[curIndex + i].index + "; text: " + res[curIndex + i].text;
        fragement.appendChild(li);
      }

      ul.appendChild(fragement);
      loop(curTotal - pageCount, curIndex + pageCount);
    });
  }

  loop(total, curIndex);
}

简单聊一下 setTimeout 和闪屏现象
  • setTimeout的执行时间并不是确定的。在JS中,setTimeout任务被放进事件队列中,只有主线程执行完才会去检查事件队列中的任务是否需要执行,因此setTimeout的实际执行时间可能会比其设定的时间晚一些。
  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的刷新频率可能会不同,而setTimeout只能设置一个固定时间间隔,这个时间不一定和屏幕的刷新时间相同。

以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致。
在setTimeout中对dom进行操作,必须要等到屏幕下次绘制时才能更新到屏幕上,如果两者步调不一致,就可能导致中间某一帧的操作被跨越过去,而直接更新下一帧的元素,从而导致丢帧现象。

方案二:requestAnimationFrame

与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。

如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象。

function request() { 
   
  let result = [];
  for (let i = 0; i < 100000; i++) { 
   
    result.push({ 
   
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => { 
   
  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  function loop(curTotal, curIndex) { 
   

    let pageCount = Math.min(curTotal , once);

    requestAnimationFrame(function () { 
   
      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) { 
   
        let li = document.createElement("li");
        li.innerText =
          "index: " + res[curIndex + i].index + "; text: " + res[curIndex + i].text;
        fragement.appendChild(li);
      }

      ul.appendChild(fragement);
      loop(curTotal - pageCount, curIndex + pageCount);
    });
  }

  loop(total, curIndex);
}

方案三:setTimeout + Generator

function request() { 
   
  let result = [];
  for (let i = 0; i < 100000; i++) { 
   
    result.push({ 
   
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => { 
   
  function ts(gen) { 
   
    if (typeof gen === "function") gen = gen();
    if (!gen || typeof gen.next !== "function") return;
    return function next() { 
   
      const res = gen.next();
      if (res.done) return;
      requestAnimationFrame(next);
    };
  }

  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  ts(addDom(total))();

  function* addDom(curTotal) { 
   
    while (curTotal > 0) { 
   
      let pageCount = Math.min(curTotal , once);

      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) { 
   
        let li = document.createElement("li");
        li.innerText =
          "index: " +
          res[curIndex + i].index +
          "; text: " +
          res[curIndex + i].text;
        fragement.appendChild(li);
      }
      ul.appendChild(fragement);
      curIndex = curIndex + pageCount;
      curTotal = curTotal - pageCount;
      yield;
    }
  }

参考:
「前端进阶」高性能渲染十万条数据(时间分片)
时间切片(Time Slicing)

今天的文章时间切片教程_切片软件手机版「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/81575.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注