首先这是一个代码运行计算大量逻辑的优化。
(1)场景
- 当JS中存在大量数据的逻辑计算时
- 非静态资源优化
- 代码层次上的优化
(2)工具
Chrome Devtools 的performance
, 在浏览器按下F12就可以找到。
(3)情况
当我们的页面在进行大量的数据逻辑运算时,我们的页面要等待数据计算完毕后才开始渲染加载完毕。首先这是因为页面渲染它是一个宏任务,在我们的task任务里面。主线程的main包含的task里也要进行js的计算,所以就造成了阻塞
。
(4)我们创建一个页面,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<title>未优化</title>
</head>
<body>
<script>
let t1, t2 = 0
t1 = new Date() // 页面开始加载
window.onload = (event) => {
t2 = new Date() // 页面加载完毕
console.log(`页面加载完毕用时: ${t2 - t1}ms`)
}
</script>
<script>
const a = () => {
b()
}
const b = () => {
let total = 0
for(let i = 0; i < 10*10000*10000; i++) {
total += i
}
console.log('1 : ', total)
}
a()
</script>
<script>
const c = () => {
d()
}
const d = () => {
let total = 0
for(let i = 0; i < 10*10000*10000; i++) {
total += i
}
console.log('2 : ', total)
}
c()
</script>
</body>
</html>
分析
- 页面加载时间:
// 控制台输出:
1 : 499999999067109000
2 : 499999999067109000
页面加载完毕用时: 3065ms
- 通过performance工具分析
发现主线程的task宏任务里面 有两个long task。而正是这些long task 导致页面渲染时阻塞状态。
我们可以进去对应的源码查看:
这些就是我们代码在运行的时间,我们找到了运行很长时间的代码,也就是long task 对应的 b函数
和 d函数
。现在我们只要消灭它们(long task)就可以让我们的页面快速加载完毕。
(5)开始优化
- 不放在主线程跑我们的函数,用worker工作者线程去跑
- 充分利用多线程的作用
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>优化后</title>
</head>
<body>
<script>
let t1, t2 = 0
t1 = new Date() // 页面开始加载
window.onload = (event) => {
t2 = new Date() // 页面加载完毕
console.log(`页面加载完毕用时: ${t2 - t1}ms`)
}
</script>
<script>
// 用worker线程优化 promise封装 postMessage本就使用异步的方式传递参数
const runWorker = (url, num) => {
return new Promise((resolve, reject) => {
const worker = new Worker(url)
worker.postMessage(num)
worker.onmessage = (evt) => {
resolve(evt.data)
}
worker.onerror = reject
})
}
</script>
<script>
const a = () => {
b()
}
const b = () => {
// 把b函数之前的数据逻辑的处理过程交给worker.js线程中的message事件去处理
runWorker('./worker.js', 10*10000*10000).then(res => {
console.log('res 01: ', res)
}).catch(err => {
console.log('err 01: ', err)
})
}
a()
</script>
<script>
const c = () => {
d()
}
const d = () => {
// 把d函数之前的数据逻辑的处理过程交给worker02.js线程中的message事件去处理
runWorker('./worker02.js', 10*10000*10000).then(res => {
console.log('res 02: ', res)
}).catch(err => {
console.log('err 02: ', err)
})
}
c()
</script>
</body>
</html>
worker.js和worker02.js
负责写我们的计算逻辑
// 这里写b函数的处理逻辑
addEventListener('message', (evt) => {
console.log(evt)
let total = 0, num = evt.data
for (let i = 0; i < num; i++) {
total += i
}
console.log('worker.js 01 : ', total)
// 最后可以把结果postMessage回去
postMessage(total)
})
打开控制台查看:
- 页面加载后的时间:
页面加载完毕用时: 22ms
- 用performance工具分析
我们的main主线程的long task消失啦,它们都在worker线程上跑着,所以不影响我们的页面渲染啦。
总结
- 消除主线程Main的long task,
从3000多毫秒降到了20多毫秒
- worker线程充分利用了多线程的作用
- 页面渲染解析HTML时,是一个宏任务
- 掌握performance常用的功能
源码在GitHub上:获取源码点这里