首先这是一个代码运行计算大量逻辑的优化。
(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上:获取源码点这里










