时间切片(Time Slicing)

时间切片的学习

时间切片(Time Slicing)

让你的网页更丝滑(全)

让你的网页更丝滑(一)

Time-Slicing时间切片模拟淘宝移动端模块化加载方案

Measure performance with the RAIL model

repo demo

看完文章后大概有这样一个结果 已有如下 Code 怎么才能结合到实际场景中去呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ts (gen) {
if (typeof gen === 'function') gen = gen()
if (!gen || typeof gen.next !== 'function') return
return function next() {
const start = performance.now()
let res = null
do {
res = gen.next()
} while(!res.done && performance.now() - start < 25)

if (res.done) return
setTimeout(next)
}
}

然后 Search 到了这样一片文章学习 Time-Slicing时间切片模拟淘宝移动端模块化加载方案

像素管道的五步 具体的在前面几篇文章介绍的非常详细了

为了保证让用户觉得流畅 我们需要保证没16.7ms就要传输新的一帧在屏幕上

所以为了保障总时间在16.7ms内 我们首先需要保障Javascript部分执行时间要小于10ms 因为浏览器执行渲染等也是有时间消耗的 所以我们预留6.7ms

更多的优化 让你的网页更丝滑(全)

避免丢帧 我们通常会使用 Timer 去改变一些样式 但是会发现中间会有16ms没有输出 这16ms就丢帧了 因为 Timer 不能保证函数在每一帧最开始执行 保证不了函数执行濒临就会导致这个问题(可能看起来卡卡的掉内容)

所以需要使用 requestAnimationFrame 来解决这个问题 使用它来触发函数 保证函数在每一帧最开始执行

所以我稍微改动了点方法 25 修改 为了 10 保证JavaScript 在 10ms 内执行 为什么这么修改 上面有介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
setTimeout

function ts(gen) {
if (typeof gen === 'function') gen = gen()
if (!gen || typeof gen.next !== 'function') return
return function next() {
const start = performance.now()
let res = null
do {
res = gen.next()
} while (!res.done && performance.now() - start < 10)

if (res.done) return
requestAnimationFrame(next)
}
}

setTimeout 修改为 requestAnimationFrame 然后 requestAnimationFrame 降级处理 这里没有考虑 this 问题 因为只是一个 Demo 简单的来

这里使用 Vue 快速的启动

1
2
3
4
5
6
7
8
<div id="app">
{{ message }}
<div>
<span v-for="(item, index) in list" :key="index" style="font-size: 10px;">
{{ item }}
</span>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
let requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
setTimeout

function ts(gen) {
if (typeof gen === 'function') gen = gen()
if (!gen || typeof gen.next !== 'function') return
return function next() {
const start = performance.now()
let res = null
do {
res = gen.next()
} while (!res.done && performance.now() - start < 10)

if (res.done) return
requestAnimationFrame(next)
}
}

const app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
list: []
},
created() {
this.getData1()
},
methods: {
// 模拟普通的加载然后赋值
getData() {
setTimeout(() => {
this.list = [...new Array(5000).keys()]
}, 300)
},
// 使用切片进行赋值
getData1() {
console.log('start')
setTimeout(() => {
let list = [...new Array(5000).keys()]
console.log('ts', ts)
const _this = this
console.log('request end')
ts(function* () {
console.log('start push')
for (let i = 0, len = list.length; i < len; i++) {
_this.list.push(list[i])
console.log('list')
yield
}
console.log('done')
})()

}, 300)
console.log('other work')

}
}
})

before

Imgur

after

Imgur

Imgur

这是 tb 的截图

Imgur

时间切片目的是为了不阻塞主线程儿实现的目的的技术手段 将一个长任务拆分成很多的小任务分散在宏任务队列中执行

使用时间切片的缺点是,任务运行的总时间变长了,这是因为它每处理完一个小任务后,主线程会空闲出来,并且在下一个小任务开始处理之前有一小段延迟。

但是为了避免卡死浏览器,这种取舍是很有必要的。 – 摘抄

Measure performance with the RAIL model 部分学习 – Google 翻译摘抄

ms
0 to 16 ms 用户非常擅长跟踪运动,当动画不流畅时,他们会不喜欢它。只要每秒渲染60个新帧,他们就能感觉到动画的流畅性。每帧16毫秒,包括浏览器将新帧绘制到屏幕上所花费的时间,而应用程序大约需要10毫秒才能生成一帧。
0 to 100 ms 在此时间窗口内响应用户的操作,用户会感觉到结果是即时的。再也没有了,作用与反应之间的联系就被打破了。
100 to 1000 ms 在此窗口中,事物感觉成为任务自然连续的过程的一部分。对于网络上的大多数用户而言,加载页面或更改视图是一项任务。
1000 ms or more 超过1000毫秒(1秒)后,用户将失去对执行任务的关注。
10000 ms or more 超过10000毫秒(10秒),用户会感到沮丧,并可能放弃任务。他们可能会或可能不会稍后再回来。

在50ms内处理事件 目标是从输入到输出总时间是100毫秒以内,用户才会觉得流畅

50毫秒还是100毫秒?

目标是在100毫秒内响应输入,为什么我们的预算只有50毫秒?这是因为除了输入处理外,通常还要执行其他工作,并且该工作会占用一部分时间来接受可接受的输入响应。如果应用程序在空闲时间以建议的50 ms块执行工作,则意味着输入在这些工作块之一中发生时最多可以排队50 ms。考虑到这一点,可以安全地假设只有剩余的50 ms可用于实际输入处理。下图显示了这种效果,该图显示了如何在空闲任务期间接收到的输入排队,从而减少了可用处理时间: – Google 翻译摘抄

Imgur

可以看一下这个粉色的地方,从input到response总时间是100毫秒,红色区域是被阻塞的部分,黄色是函数执行的时间和时机,你会发现我这两个任务都保持在50毫秒以内的情况下,我可以保证我的总时间是100毫秒以内完成的,这个50毫秒不是我定的,W3C性能工作组有一个Longtask规范也对这种情况做了规定。 – 摘抄

Imgur

这个规范就规定所有的任务,包括函数执行,包括什么都算上,不能超过50毫秒,超过50毫秒就被定义为长任务,所谓长任务就是执行时间过长的任务,这是不合理的,应该被解决的任务。性能监控一般都会通过图中的代码来监控与捕获长任务,可以看到这个entryType是longtask的。 – 摘抄

PerformanceObserver MDN

Imgur

总结一下,如何让用户感觉流畅?就是响应时间保持在100毫秒以内,动画要16.7毫秒传输一帧到屏幕上,空闲任务不能超过50毫秒,其实不只是空闲任务,所有任务都不能超过50毫秒,加载时间是1000毫秒,所谓的页面秒开就是从这里来的。这四个单词的首字母加在一起组成一个单词叫RAIL,这是一个术语,它代表以用户为中心的性能模型,我们刚才讲的也是这个话题,感兴趣大家可以回去查一下。 – 摘抄

打赏测试