美文网首页
时间分片

时间分片

作者: 前端精髓 | 来源:发表于2020-03-14 12:02 被阅读0次

《浏览器UI线程更新机制》文章中介绍大多数浏览器在 JavaScript 运行时停止 UI 线程队列中的任务,也就是说 JavaScript 任务必须尽快结束,以免对用户体验造成不良影响。

尽管你尽了最大努力,还是有一些 JavaScript 任务因为复杂性原因不能在 100 毫秒或更少时间内完成。这种情况下,理想方法是让出对 UI 线程的控制,使 UI 更新可以进行。让出控制意味着停止 JavaScript 运行,给 UI 线程机会进行更新,然后再继续运行 JavaScript。于是 JavaScript 定时器进入了我们的视野。

定时器与 UI 线程交互的方式有助于分解长运行脚本成为较短的片断。调用 setTimeout() 或 setInterval() 告诉 JavaScript 引擎等待一定时间然后将 JavaScript 任务添加到 UI 队列中。例如:

function greeting() {
  alert("Hello world!");
}
setTimeout(greeting, 250);

此代码将在 250 毫秒之后,向 UI 队列插入一个 JavaScript 任务运行 greeting() 函数。在那个点之前,所有其他 UI 更新和 JavaScript 任务都在运行。请记住,第二个参数指什么时候应当将任务添加到 UI 队列之中,并不是说那时代码将被执行。这个任务必须等到队列中的其他任务都执行之后才能被执行。

一个常见的长运行脚本就是循环占用了太长的运行时间。那么定时器就是你的下一个优化步骤。其基本方法是将循环工作分解到定时器序列中。典型的循环模式如下:

for (var i = 0, len = items.length; i < len; i++) {
  process(items[i]);
}

这样的循环结构运行时间过长的原因有二,process() 的复杂度,items 的大小,或两者兼有。

此处理过程必须是同步处理吗?数据必须按顺序处理吗?如果这两个回答都是“否”,那么代码将适于使用定时器分解工作。一种基本异步代码模式如下:

function processArray(items, process, callback) {
  var todo = items.concat();
  function updateProgress () {
    if (todo.length > 0) {
      process(todo.shift());
      setTimeout(updateProgress, 25);
    } else {
      callback()
    }
  }
  updateProgress()
}
// 用例
processArray([1, 2, 3, 4], (item) => {
  console.log('当前项', item)
}, () => {
  console.log('执行完成')
})

在 Windows 系统上定时器分辨率为 15 毫秒,也就是说一个值为 15 的定时器延时将根据最后一次系统时间刷新而转换为 0 或者 15。设置定时器延时小于 15 将在 Internet Explorer 中导致浏览器锁定,所以最小值建议为 25 毫秒(实际时间是 15 或 30)以确保至少 15 毫秒延迟。

我们通常将一个任务分解成一系列子任务。如果一个函数运行时间太长,那么查看它是否可以分解成一系列能够短时间完成的较小的函数。可将一行代码简单地看作一个原子任务,多行代码组合在一起构成一个独立任务。某些函数可基于函数调用进行拆分。

其实上面代码存在一个问题,就是定时器的间隔时间问题,我设置的是25毫秒,也就是认为最快是25毫秒可以完成UI更新,但是如果UI更新比25毫秒还要快,那么就需要等待一段时间,这个等待时间是没有必要的,所以希望的是UI更新完成之后就立马执行。而实现的关键是两个新API。

requestIdleCallback 事件循环空闲期的回调函数

requestAnimationFrame 在UI更新完成之后的回调

所以根据实际情况去选择API,在我们任务分解的过程中我们可以把定时器改成 requestAnimationFrame,如果考虑兼容问题,那还是使用定时器吧。

function processArray(items, process, callback) {
  var todo = items.concat();
  function updateProgress () {
    if (todo.length > 0) {
      process(todo.shift());
      requestAnimationFrame(updateProgress)
    } else {
      callback()
    }
  }
  updateProgress()
}

解决同步阻塞的方法,通常有两种: 异步 与 任务分割。而 React Fiber 便是为了实现任务分割而诞生的。

Fiber 其实可以算是一种编程思想,在其它语言中也有许多应用(Ruby Fiber)。核心思想是任务拆分和协同,主动把执行权交给主线程,使主线程有时间空挡处理其他高优先级任务。当遇到进程阻塞的问题时,任务分割、异步调用和缓存策略是三个显著的解决思路。

相关文章

  • 时间分片

    在《浏览器UI线程更新机制》文章中介绍大多数浏览器在 JavaScript 运行时停止 UI 线程队列中的任务,也...

  • React Fiber 任务分片 & 时间分片

    源码基于react@16.13.1 Fiber 是一个工作单元,它的引入是react实现任务分片和时间分片的基础。...

  • 十万条数据,如何渲染

    简单DOM 核心:时间分片(分片 append 进 html) 1、setTimeout -> 会有白屏(因为执行...

  • ShardingSphere官网及总结

    中文文档 分片 分片算法 精确分片算法 范围分片算法 复合分片算法 Hint分片算法 分片策略 标准分片策略 复合...

  • 221102 小回笼

    说不清是闹铃分片了时间时间分片了睡眠或者睡眠分片了他的一天倦意规整如正弦闭上眼,他就能测出振幅与相位。甚至倍频也逃...

  • 日知录1-数据分片模型和路由算法

    分片模型: 先将数据映射到分片;再将分片映射到机器;都是多对一的关系。 分片策略:哈希分片和范围分片。 哈希分片通...

  • MongoDB 集群

    分片群集 MongoDB分片群集由以下组件组成: 分片:每个分片包含分片数据。每个分片都可以部署为副本集。 mon...

  • number_of_shards的理解

    分片理论知识 分片数量总分片数=主分片数(副分片数+1)"settings": {"number_of_shard...

  • MongoDB分片群集组件

    MongoDB分片群集包含以下组件: 分片:每个分片包含分片数据的子集。从MongoDB 3.6开始,必须将分片部...

  • 第三章 索引、文档与Mapping

    一、核心概念 节点 节点就是一个ES的进程 分片 分片分为主分片与副本分片 主分片负责写入数据 副本分片只负责查询...

网友评论

      本文标题:时间分片

      本文链接:https://www.haomeiwen.com/subject/ljlvshtx.html